Chapter 13 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
โ ๏ธ Sensor Data Not VerifiedSTM32CubeIDE์์ Build โ Download โ Run๊น์ง ์ค์ NUCLEO-F439ZI์์ ์ํํ๋ค.
๋ค๋ง ADXL345 I2C module์ด ์์ด์DEVID, X/Y/Z acceleration data๋ ์์ง ๊ฒ์ฆํ์ง ์์๋ค.
Ch12์์ SPI๋ก ์ฐ๊ฒฐํ๋ ค ํ๋ ADXL345๋ฅผ ์ด๋ฒ์๋ I2C1์ผ๋ก ํต์ ํ๋ค.
STM32F439ZI
โ
โ I2C1
โ
โโโ PB8 โ SCL
โโโ PB9 โ SDA
โ
โผ
ADXL345
์ค์ :
I2C : I2C1
SCL : PB8
SDA : PB9
AF : AF4
Clock : 16 MHz APB1
Bus Speed: 100 kHz
Mode : Standard Mode
Ch12 SPI:
SCK
MOSI
MISO
CS
Ch13 I2C:
SCL
SDA
I2C์์๋ ์ฌ๋ฌ slave๊ฐ ๊ฐ์ ๋ ์ ์ ๊ณต์ ํ๊ณ ์ฃผ์๋ก ๊ตฌ๋ถ๋๋ค.
Master
โ
โโโโโโโโโ SCL โโโโโโโฌโโโโ Slave A
โ โโโโโ Slave B
โ โโโโโ Slave C
โ
โโโโโโโโโ SDA โโโโโโโดโโโโ
ADXL345์ register map ์์ฒด๋ SPI์ I2C์์ ๋์ผํ๋ค.
๋ฌ๋ผ์ง๋ ๊ฒ์:
Sensor Register
โ
์ด๋ป๊ฒ ์ ๊ทผํ๋๋
์ฆ transport protocol์ด๋ค.
์ด๋ฒ ์ค์ต:
| Pin | Function | AF |
|---|---|---|
| PB8 | I2C1_SCL | AF4 |
| PB9 | I2C1_SDA | AF4 |
ํ์ฌ Bare-Metal project:
APB1 Clock = 16 MHz
Standard Mode:
Target SCL = 100 kHz
๊ธฐ์กด ์ค์ต ๊ณ์ฐ:
CCR = 80
์๋ํ๋ฉด Standard Mode์์๋:
fSCL = fPCLK1 / (2 ร CCR)
๋ฐ๋ผ์:
100,000
=
16,000,000 / (2 ร CCR)
์ ๋ฆฌ:
CCR = 80
Standard Mode maximum rise time:
1000 ns
16 MHz clock period:
62.5 ns
๋ฐ๋ผ์:
TRISE
=
1000 / 62.5 + 1
=
17
๊ฒฐ๊ณผ:
I2C1->CCR = 80;
I2C1->TRISE = 17;I2C์์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ด๋ค.
START
โ
Slave Address + W
โ
ADDR
โ
Register Address
โ
Data
โ
STOP
ํน์ register๋ฅผ ์ฝ์ ๋:
START
โ
Slave Address + W
โ
Register Address
โ
Repeated START
โ
Slave Address + R
โ
Read Data
โ
NACK
โ
STOP
์ฆ register address๋ฅผ ๋จผ์ Write ๋ฐฉํฅ์ผ๋ก ์๋ ค์ค ๋ค Read ๋ฐฉํฅ์ผ๋ก ๋ค์ ์ ํํ๋ค.
ํ์ฌ ๋ณด์กด๋ ์๋ณธ์์ ํ์คํ ํ์ธ๋๋ ํต์ฌ ํจ์:
i2c1_init()
i2c1_start()
i2c1_byte_read()
ํ์ฌ Notion์ ํจ์ ํธ์ถ๋ง ๋จ์ ์๊ณ ๋ณธ๋ฌธ์ด ์๋ ํจ์:
i2c1_gpio_init()
i2c1_write_byte()
i2c1_stop()
๋ฐ๋ผ์ ์๋์์๋ ํ์ธ๋ ์ฝ๋๋ง ์๋ณธ ์ฝ๋๋ก ๊ธฐ๋กํ๋ค.
Wiki์์๋ ํ์ธ๋ ํจ์ interface๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌํ๋ค.
#ifndef I2C_H__
#define I2C_H__
#include <stdint.h>
#include "stm32f4xx.h"
/*
* Initialize I2C1.
*
* PB8 โ SCL
* PB9 โ SDA
* AF4
* 100 kHz Standard Mode
*/
void i2c1_init(void);
/*
* Read one register byte from an I2C slave.
*
* saddr:
* 7-bit slave address
*
* maddr:
* internal register/sub-address
*
* data:
* destination for received byte
*/
void i2c1_byte_read(uint8_t saddr,
uint8_t maddr,
uint8_t *data);
#endif#include "i2c.h"
/*
* ============================================================
* Timing Configuration
* ============================================================
*/
/*
* APB1 clock:
*
* 16 MHz
*
* Standard Mode target:
*
* 100 kHz
*
* fSCL = fPCLK1 / (2 ร CCR)
*
* 100 kHz
* =
* 16 MHz / (2 ร 80)
*/
#define I2C_100KHZ 80U
/*
* Standard Mode maximum rise time:
*
* 1000 ns
*
* APB1 clock:
*
* 16 MHz
* = 62.5 ns / cycle
*
* TRISE:
*
* 1000 / 62.5 + 1
* = 17
*/
#define SD_MODE_MAX_RISE_TIME 17U
/*
* ============================================================
* Register Bit Definitions
* ============================================================
*/
/*
* RCC APB1ENR bit21:
*
* I2C1 clock enable.
*/
#define I2C1EN (1U << 21)
/*
* CR1 bit0:
*
* PE = Peripheral Enable
*/
#define CR1_PE (1U << 0)
/*
* CR1 bit8:
*
* START generation.
*/
#define CR1_START (1U << 8)
/*
* CR1 bit9:
*
* STOP generation.
*/
#define CR1_STOP (1U << 9)
/*
* CR1 bit10:
*
* ACK control.
*/
#define CR1_ACK (1U << 10)
/*
* CR1 bit15:
*
* Software Reset.
*/
#define CR1_SWRST (1U << 15)
/*
* CR2 FREQ field:
*
* APB1 clock = 16 MHz
*/
#define CR2_FREQ_16MHZ 16U
/*
* SR1 bit0:
*
* SB = Start Bit generated.
*/
#define SR1_SB (1U << 0)
/*
* SR1 bit1:
*
* ADDR = Address sent/matched.
*/
#define SR1_ADDR (1U << 1)
/*
* SR1 bit6:
*
* RXNE = Receive Data Register Not Empty.
*/
#define SR1_RXNE (1U << 6)
/*
* SR2 bit1:
*
* BUSY = Communication ongoing on the bus.
*/
#define SR2_BUSY (1U << 1)
/*
* ============================================================
* Private Functions
* ============================================================
*
* Their complete archived source body is not present
* in the current Notion record.
*/
static void i2c1_gpio_init(void);
static void i2c1_start(uint8_t saddr, uint8_t rw);
static void i2c1_write_byte(uint8_t data);
static void i2c1_stop(void);void i2c1_init(void)
{
/*
* --------------------------------------------------------
* Step 1. Configure PB8 / PB9
* --------------------------------------------------------
*
* Expected configuration:
*
* PB8 โ I2C1_SCL
* PB9 โ I2C1_SDA
* AF4
*
* The complete body of i2c1_gpio_init()
* was not preserved in the current Notion source.
*/
i2c1_gpio_init();
/*
* --------------------------------------------------------
* Step 2. Enable I2C1 Clock
* --------------------------------------------------------
*
* I2C1 is connected to APB1.
*/
RCC->APB1ENR |= I2C1EN;
/*
* --------------------------------------------------------
* Step 3. Reset I2C Peripheral
* --------------------------------------------------------
*
* SWRST = 1
*
* Software-reset the internal I2C logic.
*/
I2C1->CR1 |= CR1_SWRST;
/*
* Release software reset.
*/
I2C1->CR1 &= ~CR1_SWRST;
/*
* --------------------------------------------------------
* Step 4. Tell I2C Hardware the APB1 Frequency
* --------------------------------------------------------
*
* CR2 FREQ field:
*
* 16
*
* means:
*
* APB1 = 16 MHz
*
* This value is used internally by I2C timing logic.
*/
I2C1->CR2 = CR2_FREQ_16MHZ;
/*
* --------------------------------------------------------
* Step 5. Configure 100 kHz SCL
* --------------------------------------------------------
*
* Standard Mode:
*
* CCR = 80
*/
I2C1->CCR = I2C_100KHZ;
/*
* --------------------------------------------------------
* Step 6. Configure Maximum Rise Time
* --------------------------------------------------------
*
* Standard Mode:
*
* TRISE = 17
*/
I2C1->TRISE = SD_MODE_MAX_RISE_TIME;
/*
* --------------------------------------------------------
* Step 7. Enable I2C1
* --------------------------------------------------------
*
* PE = 1
*/
I2C1->CR1 |= CR1_PE;
}static void i2c1_start(uint8_t saddr, uint8_t rw)
{
/*
* --------------------------------------------------------
* Step 1. Wait Until Bus Is Free
* --------------------------------------------------------
*
* SR2.BUSY:
*
* 1 โ bus currently in use
* 0 โ bus free
*/
while (I2C1->SR2 & SR2_BUSY)
{
}
/*
* --------------------------------------------------------
* Step 2. Enable ACK
* --------------------------------------------------------
*
* ACK is enabled by default before
* beginning the transaction.
*/
I2C1->CR1 |= CR1_ACK;
/*
* --------------------------------------------------------
* Step 3. Generate START
* --------------------------------------------------------
*
* Hardware generates the I2C START condition.
*/
I2C1->CR1 |= CR1_START;
/*
* --------------------------------------------------------
* Step 4. Wait for Start Bit
* --------------------------------------------------------
*
* SR1.SB = 1
*
* confirms START has been generated.
*/
while (!(I2C1->SR1 & SR1_SB))
{
}
/*
* --------------------------------------------------------
* Step 5. Send Slave Address + R/W Bit
* --------------------------------------------------------
*
* I2C uses a 7-bit slave address.
*
* Transmission byte:
*
* [A6 A5 A4 A3 A2 A1 A0 R/W]
*
* Therefore:
*
* saddr << 1
*
* creates space for R/W in bit0.
*/
I2C1->DR =
rw
? (uint8_t)((saddr << 1) | 1U)
: (uint8_t)(saddr << 1);
/*
* --------------------------------------------------------
* Step 6. Wait for Address Phase Complete
* --------------------------------------------------------
*/
while (!(I2C1->SR1 & SR1_ADDR))
{
}
/*
* --------------------------------------------------------
* Step 7. Clear ADDR Flag
* --------------------------------------------------------
*
* STM32 I2C has a special hardware-defined sequence:
*
* Read SR1
* โ
* Read SR2
* โ
* ADDR cleared
*
* The returned values themselves are not needed.
*
* The READ operation is required for its
* hardware side effect.
*/
(void)I2C1->SR1;
(void)I2C1->SR2;
}์๋ฅผ ๋ค์ด ADXL345์ 7-bit slave address๊ฐ:
0x53
์ด๋ผ๋ฉด binary:
1010011
I2C address phase์์๋ ๋ง์ง๋ง bit๊ฐ R/W๋ค.
๊ทธ๋์:
7-bit address
1010011
โ << 1
10100110
Write:
10100110
Read:
10100111
์ฆ:
(saddr << 1)
๊ณผ:
(saddr << 1) | 1U
๋ก ๊ตฌ๋ถํ๋ค.
ํน์ sensor register๋ฅผ ํ byte ์ฝ๋ ํต์ฌ ์ฝ๋๋ค.
void i2c1_byte_read(uint8_t saddr,
uint8_t maddr,
uint8_t *data)
{
/*
* ========================================================
* Phase 1
* Tell the slave WHICH register we want to read
* ========================================================
*/
/*
* --------------------------------------------------------
* Step 1. START + Slave Address in WRITE Mode
* --------------------------------------------------------
*
* rw = 0
*
* because the first phase sends the sensor's
* internal register address.
*/
i2c1_start(saddr, 0U);
/*
* --------------------------------------------------------
* Step 2. Send Sensor Register Address
* --------------------------------------------------------
*
* Example:
*
* maddr = DEVID register address
*
* This tells ADXL345 which internal register
* will be accessed next.
*
* The complete archived implementation of
* i2c1_write_byte() is currently unavailable.
*/
i2c1_write_byte(maddr);
/*
* ========================================================
* Phase 2
* Change Direction: WRITE โ READ
* ========================================================
*/
/*
* --------------------------------------------------------
* Step 3. Generate Repeated START
* --------------------------------------------------------
*
* Important:
*
* No STOP is generated between register-address
* write and data read.
*
* This keeps ownership of the bus.
*/
I2C1->CR1 |= CR1_START;
/*
* Wait until Repeated START is generated.
*/
while (!(I2C1->SR1 & SR1_SB))
{
}
/*
* --------------------------------------------------------
* Step 4. Send Slave Address in READ Mode
* --------------------------------------------------------
*
* Address format:
*
* (saddr << 1) | 1
*
* LSB = 1
*
* means READ.
*/
I2C1->DR =
(uint8_t)((saddr << 1) | 1U);
/*
* Wait until address phase completes.
*/
while (!(I2C1->SR1 & SR1_ADDR))
{
}
/*
* ========================================================
* Phase 3
* Receive One Byte
* ========================================================
*/
/*
* --------------------------------------------------------
* Step 5. Disable ACK Before Receiving Last Byte
* --------------------------------------------------------
*
* This read operation receives exactly ONE byte.
*
* Therefore the first byte is also the last byte.
*
* ACK = 0 means:
*
* Do not request another byte.
*
* The NACK behavior must be prepared BEFORE
* the final byte is completely received.
*/
I2C1->CR1 &= ~CR1_ACK;
/*
* --------------------------------------------------------
* Step 6. Clear ADDR
* --------------------------------------------------------
*
* Again:
*
* SR1 read
* โ
* SR2 read
* โ
* ADDR clear
*/
(void)I2C1->SR1;
(void)I2C1->SR2;
/*
* --------------------------------------------------------
* Step 7. Generate STOP
* --------------------------------------------------------
*
* Because this is the last receive byte,
* STOP is prepared before reading DR.
*
* The complete archived body of i2c1_stop()
* is not present in the current source record.
*/
i2c1_stop();
/*
* --------------------------------------------------------
* Step 8. Wait for Received Data
* --------------------------------------------------------
*
* RXNE = 1
*
* means one byte is available in DR.
*/
while (!(I2C1->SR1 & SR1_RXNE))
{
}
/*
* --------------------------------------------------------
* Step 9. Read Data
* --------------------------------------------------------
*
* Store the received sensor register value
* into the caller's variable.
*/
*data = (uint8_t)I2C1->DR;
}์ START๋ฅผ ๋ ๋ฒ ์ฐ๋๊ฐ?
ํน์ register๋ฅผ ์ฝ์ผ๋ ค๋ฉด ๋จผ์ :
์ด๋ register?
๋ฅผ slave์๊ฒ ์๋ ค์ค์ผ ํ๋ค.
๊ทธ๋์ ์ฒซ transaction์ Write๋ค.
START
โ
Slave + W
โ
Register Address
๊ทธ๋ค์ ์ค์ data๋ฅผ ์ฝ๊ธฐ ์ํด direction์ ๋ฐ๊พผ๋ค.
Repeated START
โ
Slave + R
โ
Data
์ ์ฒด:
START
โ
Address + W
โ
Register Address
โ
Repeated START
โ
Address + R
โ
Data
โ
STOP
STOP์ ์ค๊ฐ์ ๋ณด๋ด์ง ์๊ธฐ ๋๋ฌธ์ ํ๋์ ์ฐ๊ฒฐ๋ register-read transaction์ผ๋ก ์ ์งํ ์ ์๋ค.
์ด ๋ถ๋ถ:
(void)I2C1->SR1;
(void)I2C1->SR2;
์ ๊ฐ์ ์ป๊ธฐ ์ํด ์ฝ๋ ๊ฒ์ด ์๋๋ค.
STM32 I2C hardware์์ ADDR flag clear ์กฐ๊ฑด์ด:
Read SR1
โ
Read SR2
์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ:
register read
=
hardware command ์ญํ
๋ ํ ์ ์๋ค.
SR2๋ฅผ ์ฝ์ง ์์ผ๋ฉด ADDR๊ฐ ๊ณ์ set๋์ด ์ดํ state๊ฐ ์งํ๋์ง ์๋ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค.
Slave๊ฐ byte ํ๋๋ฅผ ๋ณด๋ด๊ณ ๋๋ฉด master๋:
ACK
โ ๋ค์ byte ๋ ๋ณด๋ด
NACK
โ ์ด์ ๊ทธ๋ง
์ ์๋ ค์ค๋ค.
ํ byte๋ง ์ฝ๋ ์ด๋ฒ ํจ์์์๋:
I2C1->CR1 &= ~CR1_ACK;๋ฅผ data๋ฅผ ์ฝ๊ธฐ ์ ์ ์ํํ๋ค.
์๋ํ๋ฉด ๋ง์ง๋ง byte์ ๋ํ NACK timing์ hardware์ ๋ฏธ๋ฆฌ ์๋ ค์ค์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋จ์ผ byte receive ํ๋ฆ:
ADDR complete
โ
ACK = 0
โ
ADDR clear
โ
STOP request
โ
RXNE
โ
Read DR
์ฒ์ ๋ณด๋ฉด:
์์ง data๋ ์ ์ฝ์๋๋ฐ STOP์ ์ ๋ฒ์จ ๋ณด๋ด์ง?
๋ผ๊ณ ์๊ฐํ๊ธฐ ์ฝ๋ค.
ํ์ง๋ง I2C receive์์๋ ACK/NACK ๋ฐ STOP์ timing์ด ์ค์ bus protocol๊ณผ ๋ง์์ผ ํด์ ๋ง์ง๋ง byte ์ ์ ์ข ๋ฃ ์กฐ๊ฑด์ ์ค๋นํ๋ค.
์ผ์ register:
DEVID
POWER_CTL
DATA_FORMAT
DATAX
DATAY
DATAZ
๋ฑ์ ๊ทธ๋๋ก๋ค.
CS LOW
โ
Command Address
โ
MOSI / MISO
โ
CS HIGH
START
โ
Slave Address
โ
Register Address
โ
Repeated START
โ
Read
โ
STOP
์ฆ:
ADXL345 Driver Logic
๊ณผ:
Communication Transport
๋ฅผ ๋ถ๋ฆฌํด์ ์๊ฐํ๋ ๊ฒ์ด ์ค์ํ๋ค.
์ด๋ฒ chapter ๋ชฉํ:
ADXL345
โ
I2C Register Access
โ
DEVID
โ
X/Y/Z
โ
UART
ํ์ง๋ง ํ์ฌ ๋ณด์กด๋ ๊ธฐ๋ก์๋ I2C low-level ํต์ฌ ํจ์๋ง ์๊ณ application ์ ์ฒด source๋ ๋จ์ ์์ง ์๋ค.
๋ฐ๋ผ์:
DEVID Read
X/Y/Z Read
UART Print
์ฝ๋๋ ์ฌ์ค์ต ์ ์ค์ source ๊ธฐ์ค์ผ๋ก ์ถ๊ฐํ๋ค.
๊ธฐ์กด ์ค์ต ๊ฒฐ๊ณผ:
Build : PASS
Download : PASS
Run : PASS
Sensor Data Verification:
NOT COMPLETE
arm-none-eabi-size:
text = 2632 bytes
data = 0 bytes
bss = 2560 bytes
๊ฒฝ๊ณ ์์ด build๋์๋ค. ์ผ์๊ฐ ์ฐ๊ฒฐ๋์ง ์์๊ธฐ ๋๋ฌธ์ DEVID์ acceleration ๊ฐ ์์ฒด๋ ํ์ธํ์ง ๋ชปํ๋ค.
NUCLEO-F439ZI ADXL345
PB8 SCL โ SCL
PB9 SDA โ SDA
3.3V โ VCC
GND โ GND
์ค์ module์ address pin configuration๋ ํ์ธํ๋ค.
Debugger:
PB8 MODER โ AF
PB9 MODER โ AF
PB8 AF โ 4
PB9 AF โ 4
ํ์ธ:
I2C1->CR2 = 16
I2C1->CCR = 80
I2C1->TRISE = 17ํ์ธ:
CR1.PE = 1
๊ฐ์ฅ ๋จผ์ :
ADXL345 DEVID
๋ฅผ ์ฝ๋๋ค.
Expected:
0xE5
DEVID๊ฐ ๋ง์ผ๋ฉด:
GPIO configuration
I2C timing
Slave address
START
Address phase
Repeated START
ACK/NACK
Receive
๊ฐ ๋๋ถ๋ถ ์ ์์ด๋ผ๊ณ ํ๋จํ ์ ์๋ค.
๊ทธ๋ค์:
DATAX
DATAY
DATAZ
๋ฅผ ์ฝ๊ณ UART๋ก ์ถ๋ ฅํ๋ค.
while()์์ ๋ฉ์ถ๋ค๋ฉด ์ด๋ flag์์ ๋ฉ์ท๋์ง ๋จผ์ ํ์ธํ๋ค.
BUSY stuck?
โ Bus / wiring ๋ฌธ์
SB not set?
โ START generation ๋ฌธ์
ADDR not set?
โ Slave address / ACK / wiring ๋ฌธ์
ADDR never clears?
โ SR1 โ SR2 read sequence ํ์ธ
RXNE not set?
โ Slave response / receive sequence ํ์ธ
I2C๋ polling loop๊ฐ ๋ง๊ธฐ ๋๋ฌธ์:
์ด๋ while์์ ๋ฉ์ท๋๊ฐ?
๊ฐ ๋งค์ฐ ์ค์ํ debugging information์ด๋ค.
ํ์ฌ:
images/ch13_build.png
images/ch13_download.png
์ค๊ธฐ ๊ฒ์ฆ ํ:
images/ch13_wiring.jpg
images/ch13_gpio_af.png
images/ch13_i2c_timing.png
images/ch13_devid.png
images/ch13_xyz_uart.png
images/ch13_logic_analyzer.png
๊ฐ์ฅ ์ค์ํ ์ค๊ธฐ ์ฆ๊ฑฐ:
DEVID = 0xE5
๊ทธ๋ฆฌ๊ณ ๊ฐ๋ฅํ๋ฉด logic analyzer์:
START
Address
ACK
Register Address
Repeated START
Data
NACK
STOP
waveform์ด๋ค.
16 MHz APB1
โ
CR2 = 16
โ
CCR = 80
โ
TRISE = 17
โ
100 kHz I2C
๊ทธ๋ฆฌ๊ณ register read:
START
โ
Slave + W
โ
Register Address
โ
Repeated START
โ
Slave + R
โ
NACK
โ
Data
โ
STOP
- I2C1์ PB8=SCL, PB9=SDA, AF4๋ฅผ ์ฌ์ฉํ๋ค.
- ์ด๋ฒ project๋ APB1 16MHz ๊ธฐ์ค์ด๋ค.
- Standard Mode 100kHz์์
CCR=80,TRISE=17์ ์ฌ์ฉํ๋ค. - I2C slave address๋ 7-bit์ด๊ณ ์ ์ก ์
<<1ํ LSB์ R/W๋ฅผ ๋ถ์ธ๋ค. -
SB๋ START condition ์์ฑ ์๋ฃ๋ฅผ ๋ํ๋ธ๋ค. -
ADDR๋ address phase ์๋ฃ๋ฅผ ๋ํ๋ธ๋ค. -
ADDRclear์๋ SR1 read โ SR2 read ์์๊ฐ ํ์ํ๋ค. - register read๋ Write sub-address โ Repeated START โ Read ๊ตฌ์กฐ๋ค.
- single-byte receive์์๋ ๋ง์ง๋ง byte ์ ์ ACK๋ฅผ ๋๊ณ STOP์ ์ค๋นํ๋ค.
- ํ์ฌ ์ํ๋ build/download/run PASS, sensor data ๋ฏธ๊ฒ์ฆ์ด๋ค.