Bare metal C Ch13 I2C - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
ํ ์ค ์์ฝ: I2C1์ 100 kHz Standard-mode๋ก ๊ตฌ์ฑํ๊ณ ADXL345 register๋ฅผ byte/burst ๋ฐฉ์์ผ๋ก ์ฝ๊ณ ์ด๋ค.
I2C๋ SCL๊ณผ SDA ๋ ์ ์ ์ฌ๋ฌ device๊ฐ ๊ณต์ ํ๊ณ 7-bit slave address๋ก ์ฅ์น๋ฅผ ๊ตฌ๋ถํ๋ synchronous serial bus๋ค.
I2C line์ open-drain ๋ฐฉ์์ผ๋ก ์ค์ ๋์ด ์๋ค.
Notion ๊ธฐ๋ก์ 16 MHz APB1 ๊ธฐ์ค:
CCR = 80
TRISE = 17
START
โ
Slave Address + W
โ
Register Address
โ
Repeated START
โ
Slave Address + R
โ
Data
โ
NACK / STOP
STM32 I2C์์๋ ADDR flag๋ฅผ clearํ๊ธฐ ์ํด:
(void)I2C1->SR1;
(void)I2C1->SR2;
์์๋ก ๋ register๋ฅผ ์ฝ๋๋ค.
๋ง์ง๋ง byte๋ฅผ ๋ฐ๊ธฐ ์ ์ ACK๋ฅผ ๋ด๋ฆฌ๊ณ STOP์ ์ค๋นํ๋ค. ์ด๋ ๋ง์ง๋ง byte ์ดํ ๋ ์ด์ data๋ฅผ ์๊ตฌํ์ง ์๋๋ค๋ ๊ฒ์ hardware์ ๋ฏธ๋ฆฌ ์๋ ค์ฃผ๊ธฐ ์ํ sequence๋ค.
PB8/PB9 โ AF4 + Open Drain
โ
โผ
I2C1 init
โ
โผ
100 kHz ์ค์
โ
โผ
ADXL345 configure
โ
โผ
Register Address
โ
Repeated START
โ
โผ
Burst Read
โ
โผ
X/Y/Z โ printf
Ch13_i2c/
โโโ Inc/i2c.h
โโโ Src/i2c.c
โโโ Inc/adxl345.h
โโโ Src/adxl345.c
โโโ Inc/uart.h
โโโ Src/uart.c
โโโ Inc/systick.h
โโโ Src/systick.c
โโโ Src/main.c
syscalls.c,sysmem.c, startup file, linker script, CubeIDE metadata/build output์ Ch04์์ ๋ค๋ฃฌ ๊ณตํต ๊ธฐ๋ฐ์ด๋ฏ๋ก ์ด ํ์ด์ง์์๋ ๋ฐ๋ณตํ์ง ์๋๋ค. ์๋์๋ ์ด chapter ๊ธฐ๋ฅ ๊ตฌํ์ ์ง์ ์ฌ์ฉํ source/header๋ฅผ ์ ๋ถ ๊ธฐ๋กํ๋ค.
#ifndef I2C_H
#define I2C_H
#include <stdint.h>
#include "stm32f4xx.h"
void i2c1_init(void);
void i2c1_byte_read(uint8_t saddr, uint8_t maddr, uint8_t *data);
void i2c1_burst_read(uint8_t saddr, uint8_t maddr, uint8_t n, uint8_t *data);
void i2c1_burst_write(uint8_t saddr, uint8_t maddr, uint8_t n, uint8_t *data);
#endif
#include "i2c.h"
#define GPIOBEN (1U << 1)
#define I2C1EN (1U << 21)
#define SR1_SB (1U << 0)
#define SR1_ADDR (1U << 1)
#define SR1_BTF (1U << 2)
#define SR1_RXNE (1U << 6)
#define SR1_TXE (1U << 7)
#define SR2_BUSY (1U << 1)
#define CR1_START (1U << 8)
#define CR1_STOP (1U << 9)
#define CR1_ACK (1U << 10)
#define CR1_SWRST (1U << 15)
#define CR1_PE (1U << 0)
#define CR2_FREQ_16MHZ (16U << 0)
#define I2C_100KHZ 80U
#define SD_MODE_MAX_RISE_TIME 17U
static void i2c1_gpio_init(void)
{
RCC->AHB1ENR |= GPIOBEN;
GPIOB->MODER &= ~(0x3UL << (8 * 2));
GPIOB->MODER |= (0x2UL << (8 * 2));
GPIOB->MODER &= ~(0x3UL << (9 * 2));
GPIOB->MODER |= (0x2UL << (9 * 2));
GPIOB->OTYPER |= (1UL << 8);
GPIOB->OTYPER |= (1UL << 9);
GPIOB->PUPDR &= ~(0x3UL << (8 * 2));
GPIOB->PUPDR |= (0x1UL << (8 * 2));
GPIOB->PUPDR &= ~(0x3UL << (9 * 2));
GPIOB->PUPDR |= (0x1UL << (9 * 2));
GPIOB->AFR[1] &= ~(0xFUL << 0);
GPIOB->AFR[1] |= (0x4UL << 0);
GPIOB->AFR[1] &= ~(0xFUL << 4);
GPIOB->AFR[1] |= (0x4UL << 4);
}
void i2c1_init(void)
{
i2c1_gpio_init();
RCC->APB1ENR |= I2C1EN;
I2C1->CR1 |= CR1_SWRST;
I2C1->CR1 &= ~CR1_SWRST;
I2C1->CR2 = CR2_FREQ_16MHZ;
I2C1->CCR = I2C_100KHZ;
I2C1->TRISE = SD_MODE_MAX_RISE_TIME;
I2C1->CR1 |= CR1_PE;
}
static void i2c1_start(uint8_t saddr, uint8_t rw)
{
while (I2C1->SR2 & SR2_BUSY) {}
I2C1->CR1 |= CR1_ACK;
I2C1->CR1 |= CR1_START;
while (!(I2C1->SR1 & SR1_SB)) {}
if (rw == 1U) I2C1->DR = (uint8_t)((saddr << 1) | 1U);
else I2C1->DR = (uint8_t)(saddr << 1);
while (!(I2C1->SR1 & SR1_ADDR)) {}
(void)I2C1->SR1;
(void)I2C1->SR2;
}
static void i2c1_write_byte(uint8_t data)
{
while (!(I2C1->SR1 & SR1_TXE)) {}
I2C1->DR = data;
while (!(I2C1->SR1 & SR1_BTF)) {}
}
static void i2c1_stop(void)
{
I2C1->CR1 |= CR1_STOP;
}
void i2c1_byte_read(uint8_t saddr, uint8_t maddr, uint8_t *data)
{
i2c1_start(saddr, 0U);
i2c1_write_byte(maddr);
I2C1->CR1 |= CR1_START;
while (!(I2C1->SR1 & SR1_SB)) {}
I2C1->DR = (uint8_t)((saddr << 1) | 1U);
while (!(I2C1->SR1 & SR1_ADDR)) {}
I2C1->CR1 &= ~CR1_ACK;
(void)I2C1->SR1;
(void)I2C1->SR2;
i2c1_stop();
while (!(I2C1->SR1 & SR1_RXNE)) {}
*data = (uint8_t)I2C1->DR;
}
void i2c1_burst_read(uint8_t saddr, uint8_t maddr, uint8_t n, uint8_t *data)
{
uint8_t i;
i2c1_start(saddr, 0U);
i2c1_write_byte(maddr);
I2C1->CR1 |= CR1_START;
while (!(I2C1->SR1 & SR1_SB)) {}
I2C1->DR = (uint8_t)((saddr << 1) | 1U);
while (!(I2C1->SR1 & SR1_ADDR)) {}
I2C1->CR1 |= CR1_ACK;
(void)I2C1->SR1;
(void)I2C1->SR2;
for (i = 0; i < n; i++)
{
if (i == (n - 1U))
{
I2C1->CR1 &= ~CR1_ACK;
i2c1_stop();
}
while (!(I2C1->SR1 & SR1_RXNE)) {}
data[i] = (uint8_t)I2C1->DR;
}
}
void i2c1_burst_write(uint8_t saddr, uint8_t maddr, uint8_t n, uint8_t *data)
{
uint8_t i;
i2c1_start(saddr, 0U);
i2c1_write_byte(maddr);
for (i = 0; i < n; i++) i2c1_write_byte(data[i]);
i2c1_stop();
}
#ifndef ADXL345_H_
#define ADXL345_H_
#include <stdint.h>
#include "i2c.h"
#define ADXL345_DEVICE_ADDR 0x53U
#define ADXL345_REG_DEVID 0x00U
#define ADXL345_REG_DATA_FORMAT 0x31U
#define ADXL345_REG_POWER_CTL 0x2DU
#define ADXL345_REG_DATA_START 0x32U
#define ADXL345_RANGE_4G 0x01U
#define ADXL345_RESET 0x00U
#define ADXL345_MEASURE_BIT 0x08U
void adxl_init(void);
void adxl_read(uint8_t address, uint8_t *rxdata);
void adxl_read_values(uint8_t *rxdata6);
void adxl_write(uint8_t address, uint8_t value);
#endif
#include "adxl345.h"
void adxl_init(void)
{
i2c1_init();
adxl_write(ADXL345_REG_DATA_FORMAT, ADXL345_RANGE_4G);
adxl_write(ADXL345_REG_POWER_CTL, ADXL345_RESET);
adxl_write(ADXL345_REG_POWER_CTL, ADXL345_MEASURE_BIT);
}
void adxl_read(uint8_t address, uint8_t *rxdata)
{
i2c1_byte_read(ADXL345_DEVICE_ADDR, address, rxdata);
}
void adxl_read_values(uint8_t *rxdata6)
{
i2c1_burst_read(ADXL345_DEVICE_ADDR, ADXL345_REG_DATA_START, 6U, rxdata6);
}
void adxl_write(uint8_t address, uint8_t value)
{
i2c1_burst_write(ADXL345_DEVICE_ADDR, address, 1U, &value);
}
#ifndef UART_H
#define UART_H
#include "stm32f4xx.h"
void uart_init(void);
void uart_write_string(const char *s);
int __io_putchar(int ch);
#endif
#include <stdint.h>
#include "uart.h"
#define GPIODEN (1U << 3)
#define USART3EN (1U << 18)
#define DBG_UART_BAUDRATE 115200U
#define SYS_FREQ 16000000U
#define APB1_CLK SYS_FREQ
#define CR1_TE (1U << 3)
#define CR1_UE (1U << 13)
#define SR_TXE (1U << 7)
static void uart_write(int ch);
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate);
static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate);
int __io_putchar(int ch)
{
uart_write(ch);
return ch;
}
void uart_init(void)
{
RCC->AHB1ENR |= GPIODEN;
GPIOD->MODER &= ~(0x3UL << (8 * 2));
GPIOD->MODER |= (0x2UL << (8 * 2));
GPIOD->MODER &= ~(0x3UL << (9 * 2));
GPIOD->MODER |= (0x2UL << (9 * 2));
GPIOD->AFR[1] &= ~(0xFUL << 0);
GPIOD->AFR[1] |= (0x7UL << 0); /* PD8 -> AF7 (USART3_TX) */
GPIOD->AFR[1] &= ~(0xFUL << 4);
GPIOD->AFR[1] |= (0x7UL << 4); /* PD9 -> AF7 (USART3_RX) */
RCC->APB1ENR |= USART3EN;
uart_set_baudrate(APB1_CLK, DBG_UART_BAUDRATE);
USART3->CR1 = CR1_TE;
USART3->CR1 |= CR1_UE;
}
static void uart_write(int ch)
{
while (!(USART3->SR & SR_TXE)) { }
USART3->DR = (ch & 0xFF);
}
void uart_write_string(const char *s)
{
while (*s) uart_write((int)*s++);
}
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate)
{
return (uint16_t)((periph_clk + (baudrate / 2U)) / baudrate);
}
static void uart_set_baudrate(uint32_t periph_clk, uint32_t baudrate)
{
USART3->BRR = compute_uart_bd(periph_clk, baudrate);
}
#ifndef SYSTICK_H_
#define SYSTICK_H_
#include <stdint.h>
#include "stm32f4xx.h"
void systick_msec_delay(uint32_t delay);
#endif /* SYSTICK_H_ */
#include "systick.h"
#define CTRL_ENABLE (1UL << 0)
#define CTRL_CLKSRC (1UL << 2)
#define CTRL_COUNTFLAG (1UL << 16)
/* ์ฝ์ดํด๋ญ 16MHz(๊ธฐ๋ณธ HSI) ๊ธฐ์ค 1ms๋น ํด๋ญ ์ฌ์ดํด ์ */
#define ONE_MSEC_LOAD 16000UL
void systick_msec_delay(uint32_t delay)
{
SysTick->LOAD = ONE_MSEC_LOAD - 1UL;
SysTick->VAL = 0UL;
SysTick->CTRL = CTRL_CLKSRC;
SysTick->CTRL |= CTRL_ENABLE;
for (uint32_t i = 0; i < delay; i++)
{
while ((SysTick->CTRL & CTRL_COUNTFLAG) == 0UL) { }
}
SysTick->CTRL = 0UL;
}
#include <stdio.h>
#include "adxl345.h"
/* Ch10์์ ๋ง๋ uart_init()/uart.c์ __io_putchar()๊ฐ ์ด๋ฏธ ์๋ค๊ณ ๊ฐ์ /
extern void uart_init(void);
void systick_msec_delay(uint32_t delay); / Ch08 systick.c */
int main(void)
{
uint8_t devid = 0;
uint8_t rxdata[6];
int16_t x, y, z;
uart_init();
adxl_init();
adxl_read(ADXL345_REG_DEVID, &devid);
printf("ADXL345(I2C) DEVID (expect 0xE5): 0x%02X\r\n", devid);
while (1)
{
adxl_read_values(rxdata);
x = (int16_t)((rxdata[1] << 8) | rxdata[0]);
y = (int16_t)((rxdata[3] << 8) | rxdata[2]);
z = (int16_t)((rxdata[5] << 8) | rxdata[4]);
printf("X=%d Y=%d Z=%d\r\n", x, y, z);
systick_msec_delay(200);
}
}
Notion ๊ธฐ๋ก:
STM32CubeIDE build/download/run ํ์ธ
ADXL345 I2C sensor module์ด ์์ด ์ค์
DEVID์ acceleration ๊ฐ์ ๋ฏธ๊ฒ์ฆ
text = 2632 bytes
data = 0 bytes
bss = 2560 bytes
warning ์์
์ผ์๊ฐ ์ฐ๊ฒฐ๋์ง ์์ ์กฐ๊ฑด์์๋ ACK/ADDR ๊ด๋ จ polling ๊ตฌ๊ฐ์์ ์ค์ ํต์ ์๋ฃ ์ฌ๋ถ๋ฅผ ๊ฒ์ฆํ ์ ์์ผ๋ฏ๋ก, sensor value ๊ฒ์ฆ์ ๋ณ๋๋ก ๋จ๊ฒจ๋ ์ํ๋ค.
I2C๋ START/Address/ACK/STOP sequence๊ฐ ์ค์ํ๋ค.
STM32์ ADDR clear์๋ SR1โSR2 read ์์๊ฐ ํ์ํ๋ค.
ํน์ register read์๋ Repeated START๋ฅผ ์ฌ์ฉํ๋ค.