Bare metal C Ch13 I2C - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

Ch13. I2C + ADXL345

ํ•œ ์ค„ ์š”์•ฝ: I2C1์„ 100 kHz Standard-mode๋กœ ๊ตฌ์„ฑํ•˜๊ณ  ADXL345 register๋ฅผ byte/burst ๋ฐฉ์‹์œผ๋กœ ์ฝ๊ณ  ์“ด๋‹ค.

1. ํ•ต์‹ฌ ์ด๋ก 

I2C๋Š” SCL๊ณผ SDA ๋‘ ์„ ์„ ์—ฌ๋Ÿฌ device๊ฐ€ ๊ณต์œ ํ•˜๊ณ  7-bit slave address๋กœ ์žฅ์น˜๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” synchronous serial bus๋‹ค.

์‚ฌ์šฉ ํ•€

๊ธฐ๋Šฅ | Pin -- | -- I2C1 SCL | PB8 / AF4 I2C1 SDA | PB9 / AF4

I2C line์€ open-drain ๋ฐฉ์‹์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค.

100 kHz ์„ค์ •

Notion ๊ธฐ๋ก์˜ 16 MHz APB1 ๊ธฐ์ค€:

CCR   = 80
TRISE = 17

๊ธฐ๋ณธ transaction

START
  โ†“
Slave Address + W
  โ†“
Register Address
  โ†“
Repeated START
  โ†“
Slave Address + R
  โ†“
Data
  โ†“
NACK / STOP

ADDR clear sequence

STM32 I2C์—์„œ๋Š” ADDR flag๋ฅผ clearํ•˜๊ธฐ ์œ„ํ•ด:

(void)I2C1->SR1;
(void)I2C1->SR2;

์ˆœ์„œ๋กœ ๋‘ register๋ฅผ ์ฝ๋Š”๋‹ค.

๋งˆ์ง€๋ง‰ byte ์ˆ˜์‹ 

๋งˆ์ง€๋ง‰ byte๋ฅผ ๋ฐ›๊ธฐ ์ „์— ACK๋ฅผ ๋‚ด๋ฆฌ๊ณ  STOP์„ ์ค€๋น„ํ•œ๋‹ค. ์ด๋Š” ๋งˆ์ง€๋ง‰ byte ์ดํ›„ ๋” ์ด์ƒ data๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ hardware์— ๋ฏธ๋ฆฌ ์•Œ๋ ค์ฃผ๊ธฐ ์œ„ํ•œ sequence๋‹ค.

2. ๋™์ž‘ ํ๋ฆ„

PB8/PB9 โ†’ AF4 + Open Drain
             โ”‚
             โ–ผ
          I2C1 init
             โ”‚
             โ–ผ
         100 kHz ์„ค์ •
             โ”‚
             โ–ผ
       ADXL345 configure
             โ”‚
             โ–ผ
       Register Address
             โ”‚
       Repeated START
             โ”‚
             โ–ผ
          Burst Read
             โ”‚
             โ–ผ
       X/Y/Z โ†’ printf

3. ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ


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๋ฅผ ์ „๋ถ€ ๊ธฐ๋กํ•œ๋‹ค.

4. ์ „์ฒด ์ฝ”๋“œ

Inc/i2c.h

#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

Src/i2c.c

#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-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (8 * 2));
GPIOB-&gt;MODER |=  (0x2UL &lt;&lt; (8 * 2));
GPIOB-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (9 * 2));
GPIOB-&gt;MODER |=  (0x2UL &lt;&lt; (9 * 2));

GPIOB-&gt;OTYPER |= (1UL &lt;&lt; 8);
GPIOB-&gt;OTYPER |= (1UL &lt;&lt; 9);

GPIOB-&gt;PUPDR &amp;= ~(0x3UL &lt;&lt; (8 * 2));
GPIOB-&gt;PUPDR |=  (0x1UL &lt;&lt; (8 * 2));
GPIOB-&gt;PUPDR &amp;= ~(0x3UL &lt;&lt; (9 * 2));
GPIOB-&gt;PUPDR |=  (0x1UL &lt;&lt; (9 * 2));

GPIOB-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 0);
GPIOB-&gt;AFR[1] |=  (0x4UL &lt;&lt; 0);
GPIOB-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 4);
GPIOB-&gt;AFR[1] |=  (0x4UL &lt;&lt; 4);

}

void i2c1_init(void) { i2c1_gpio_init(); RCC->APB1ENR |= I2C1EN;

I2C1-&gt;CR1 |= CR1_SWRST;
I2C1-&gt;CR1 &amp;= ~CR1_SWRST;

I2C1-&gt;CR2 = CR2_FREQ_16MHZ;
I2C1-&gt;CCR = I2C_100KHZ;
I2C1-&gt;TRISE = SD_MODE_MAX_RISE_TIME;

I2C1-&gt;CR1 |= CR1_PE;

}

static void i2c1_start(uint8_t saddr, uint8_t rw) { while (I2C1->SR2 & SR2_BUSY) {}

I2C1-&gt;CR1 |= CR1_ACK;
I2C1-&gt;CR1 |= CR1_START;
while (!(I2C1-&gt;SR1 &amp; SR1_SB)) {}

if (rw == 1U) I2C1-&gt;DR = (uint8_t)((saddr &lt;&lt; 1) | 1U);
else          I2C1-&gt;DR = (uint8_t)(saddr &lt;&lt; 1);

while (!(I2C1-&gt;SR1 &amp; SR1_ADDR)) {}
(void)I2C1-&gt;SR1;
(void)I2C1-&gt;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-&gt;CR1 |= CR1_START;
while (!(I2C1-&gt;SR1 &amp; SR1_SB)) {}
I2C1-&gt;DR = (uint8_t)((saddr &lt;&lt; 1) | 1U);
while (!(I2C1-&gt;SR1 &amp; SR1_ADDR)) {}

I2C1-&gt;CR1 &amp;= ~CR1_ACK;
(void)I2C1-&gt;SR1;
(void)I2C1-&gt;SR2;
i2c1_stop();

while (!(I2C1-&gt;SR1 &amp; SR1_RXNE)) {}
*data = (uint8_t)I2C1-&gt;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-&gt;CR1 |= CR1_START;
while (!(I2C1-&gt;SR1 &amp; SR1_SB)) {}
I2C1-&gt;DR = (uint8_t)((saddr &lt;&lt; 1) | 1U);
while (!(I2C1-&gt;SR1 &amp; SR1_ADDR)) {}

I2C1-&gt;CR1 |= CR1_ACK;
(void)I2C1-&gt;SR1;
(void)I2C1-&gt;SR2;

for (i = 0; i &lt; n; i++)
{
    if (i == (n - 1U))
    {
        I2C1-&gt;CR1 &amp;= ~CR1_ACK;
        i2c1_stop();
    }
    while (!(I2C1-&gt;SR1 &amp; SR1_RXNE)) {}
    data[i] = (uint8_t)I2C1-&gt;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 &lt; n; i++) i2c1_write_byte(data[i]);

i2c1_stop();

}

Inc/adxl345.h

#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

Src/adxl345.c

#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); }

Inc/uart.h

#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

Src/uart.c

#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-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (8 * 2));
GPIOD-&gt;MODER |=  (0x2UL &lt;&lt; (8 * 2));
GPIOD-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (9 * 2));
GPIOD-&gt;MODER |=  (0x2UL &lt;&lt; (9 * 2));

GPIOD-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 0);
GPIOD-&gt;AFR[1] |=  (0x7UL &lt;&lt; 0);   /* PD8 -&gt; AF7 (USART3_TX) */
GPIOD-&gt;AFR[1] &amp;= ~(0xFUL &lt;&lt; 4);
GPIOD-&gt;AFR[1] |=  (0x7UL &lt;&lt; 4);   /* PD9 -&gt; AF7 (USART3_RX) */

RCC-&gt;APB1ENR |= USART3EN;

uart_set_baudrate(APB1_CLK, DBG_UART_BAUDRATE);

USART3-&gt;CR1 = CR1_TE;
USART3-&gt;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); }

Inc/systick.h

#ifndef SYSTICK_H_
#define SYSTICK_H_

#include <stdint.h> #include "stm32f4xx.h"

void systick_msec_delay(uint32_t delay);

#endif /* SYSTICK_H_ */

Src/systick.c

#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 &lt; delay; i++)
{
    while ((SysTick-&gt;CTRL &amp; CTRL_COUNTFLAG) == 0UL) { }
}

SysTick-&gt;CTRL = 0UL;

}

Src/main.c

#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, &amp;devid);
printf("ADXL345(I2C) DEVID (expect 0xE5): 0x%02X\r\n", devid);

while (1)
{
    adxl_read_values(rxdata);

    x = (int16_t)((rxdata[1] &lt;&lt; 8) | rxdata[0]);
    y = (int16_t)((rxdata[3] &lt;&lt; 8) | rxdata[2]);
    z = (int16_t)((rxdata[5] &lt;&lt; 8) | rxdata[4]);

    printf("X=%d Y=%d Z=%d\r\n", x, y, z);

    systick_msec_delay(200);
}

}

5. ๊ฒ€์ฆ ์ƒํƒœ

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 ๊ฒ€์ฆ์€ ๋ณ„๋„๋กœ ๋‚จ๊ฒจ๋‘” ์ƒํƒœ๋‹ค.

6. ํ•ต์‹ฌ ์ •๋ฆฌ

  • I2C๋Š” START/Address/ACK/STOP sequence๊ฐ€ ์ค‘์š”ํ•˜๋‹ค.

  • STM32์˜ ADDR clear์—๋Š” SR1โ†’SR2 read ์ˆœ์„œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

  • ํŠน์ • register read์—๋Š” Repeated START๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ