Chapter 12 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki

12. SPI + ADXL345

⚠️ Hardware Not Verified

이 ChapterλŠ” makeλ₯Ό μ΄μš©ν•œ Compile / LinkκΉŒμ§€ ν™•μΈν–ˆλ‹€.
ADXL345 sensor module이 μ—†μ–΄μ„œ μ‹€μ œ SPI waveform, DEVID = 0xE5, X/Y/Z acceleration 값은 아직 κ²€μ¦ν•˜μ§€ μ•Šμ•˜λ‹€.


Goal

STM32F439ZI의 SPI1을 Master mode둜 μ„€μ •ν•˜κ³  ADXL345 accelerometer와 SPI ν†΅μ‹ ν•˜κΈ° μœ„ν•œ low-level driverλ₯Ό κ΅¬ν˜„ν•œλ‹€.

STM32F439ZI
    β”‚
    β”‚ SPI1
    β”‚
    β”œβ”€β”€ PA5 β†’ SCK
    β”œβ”€β”€ PA6 β†’ MISO
    β”œβ”€β”€ PA7 β†’ MOSI
    └── PA9 β†’ CS
             β”‚
             β–Ό
          ADXL345

SPI μ„€μ •:

SPI      : SPI1
Mode     : Master
SCK      : PA5
MISO     : PA6
MOSI     : PA7
CS       : PA9 GPIO
AF       : AF5
Frame    : 8-bit
Bit Order: MSB First
Mode     : SPI Mode 3
           CPOL = 1
           CPHA = 1

1. SPI 핡심

SPIλŠ” Synchronous Serial Communication 방식이닀.

UART와 달리 masterκ°€ clock을 직접 μƒμ„±ν•œλ‹€.

Master                         Slave

SCK ───────────────────────→ Clock MOSI ───────────────────────→ Data MISO ←─────────────────────── Data CS ───────────────────────→ Select

각 signal:

SCK
β†’ Serial Clock

MOSI β†’ Master Out Slave In

MISO β†’ Master In Slave Out

CS β†’ Chip Select


2. SPI1 Pin Mapping

이번 μ‹€μŠ΅:

Pin Function Mode
PA5 SPI1_SCK AF5
PA6 SPI1_MISO AF5
PA7 SPI1_MOSI AF5
PA9 ADXL345 CS GPIO Output

PA5~PA7은:

GPIO Alternate Function
        ↓
AF5
        ↓
SPI1

으둜 μ—°κ²°ν•œλ‹€.

CSλŠ” SPI hardware NSSλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  일반 GPIO output으둜 직접 μ œμ–΄ν•œλ‹€.


3. SPI Registers

이번 μ½”λ“œμ—μ„œ μ‚¬μš©ν•˜λŠ” register:

RCC->AHB1ENR
RCC->APB2ENR

GPIOA->MODER GPIOA->AFR[0] GPIOA->ODR

SPI1->CR1 SPI1->SR SPI1->DR

μ£Όμš” SPI status:

SR.RXNE
β†’ Receive buffer Not Empty

SR.TXE β†’ Transmit buffer Empty

SR.BSY β†’ SPI communication Busy


4. ADXL345 SPI Command

ADXL345 register accessμ—μ„œλŠ” address의 μƒμœ„ bitλ₯Ό command μ •λ³΄λ‘œ μ‚¬μš©ν•œλ‹€.

bit7
R/W

0 β†’ Write 1 β†’ Read

bit6 MB

0 β†’ Single Byte 1 β†’ Multi Byte

예λ₯Ό λ“€μ–΄ register read commandλŠ” κ°œλ…μ μœΌλ‘œ:

Register Address
OR
0x80
↓
Read Command

Multi-byte read라면:

Register Address
|
0x80
|
0x40

ν˜•νƒœκ°€ λœλ‹€.


5. Practice Code Status

ν˜„μž¬ 보쑴된 μ‹€μŠ΅ μ›λ³Έμ—μ„œ ν™•μΈλ˜λŠ” 파일:

Inc/
└── spi.h

Src/ └── spi.c

ADXL345 application layerλŠ” ν˜„μž¬ Notion 및 repository searchμ—μ„œ 전체 sourceλ₯Ό ν™•μΈν•˜μ§€ λͺ»ν–ˆλ‹€.

λ”°λΌμ„œ 이 Wikiμ—μ„œλŠ”:

spi.h
spi.c

λ₯Ό confirmed source둜 κΈ°λ‘ν•œλ‹€.

ν–₯ν›„ sensor module 확보 ν›„:

adxl345.h
adxl345.c
main.c

λ₯Ό μ‹€μ œ ν…ŒμŠ€νŠΈ μ½”λ“œ κΈ°μ€€μœΌλ‘œ μΆ”κ°€ν•œλ‹€.


6. spi.h

#ifndef SPI_H_
#define SPI_H_

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

/*

  • Configure GPIO pins used by SPI1.
  • PA5 β†’ SCK
  • PA6 β†’ MISO
  • PA7 β†’ MOSI
  • PA9 β†’ Software-controlled CS */ void spi_gpio_init(void);

/*

  • Configure SPI1 as Master.
  • ADXL345 communication configuration:
  • CPOL = 1
  • CPHA = 1
  • β†’ SPI Mode 3 */ void spi1_config(void);

/*

  • Transmit one or more bytes through SPI1.
  • data:
  • pointer to TX buffer
    
  • size:
  • number of bytes to send
    

*/ void spi1_transmit(uint8_t *data, uint32_t size);

/*

  • Receive one or more bytes through SPI1.
  • SPI master must generate SCK even when only
  • receiving data, therefore the implementation
  • transmits dummy bytes internally. */ void spi1_receive(uint8_t *data, uint32_t size);

/*

  • Assert chip select.
  • ADXL345 CS is active LOW. */ void cs_enable(void);

/*

  • Deassert chip select. */ void cs_disable(void);

#endif


7. spi.c

#include "spi.h"

/*

  • ============================================================
  • RCC Clock Enable Bits
  • ============================================================ */

/*

  • RCC AHB1ENR bit0
  • GPIOA clock enable.
  • SPI1 pins PA5 / PA6 / PA7 and
  • software CS PA9 all belong to GPIOA. */ #define GPIOAEN (1U << 0)

/*

  • RCC APB2ENR bit12
  • SPI1 peripheral clock enable. */ #define SPI1EN (1U << 12)

/*

  • ============================================================
  • SPI Status Register Bits
  • ============================================================ */

/*

  • SR bit0
  • RXNE = Receive buffer Not Empty
  • 0 β†’ no new received data
  • 1 β†’ received data available in DR */ #define SR_RXNE (1U << 0)

/*

  • SR bit1
  • TXE = Transmit buffer Empty
  • 0 β†’ transmit buffer not ready
  • 1 β†’ new data may be written to DR */ #define SR_TXE (1U << 1)

/*

  • SR bit7
  • BSY = Busy flag
  • 1 β†’ SPI communication still in progress
  • 0 β†’ SPI bus no longer busy */ #define SR_BSY (1U << 7)

/*

  • ============================================================
  • SPI GPIO Initialization
  • ============================================================ */

void spi_gpio_init(void) { /* * -------------------------------------------------------- * Step 1. Enable GPIOA Clock * -------------------------------------------------------- */ RCC->AHB1ENR |= GPIOAEN;

/*
 * --------------------------------------------------------
 * Step 2. PA5 β†’ Alternate Function
 * --------------------------------------------------------
 *
 * PA5:
 *
 *     SPI1_SCK
 *
 * MODER<span>[11:10]</span> = 10
 */
GPIOA-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (5 * 2));
GPIOA-&gt;MODER |=  (0x2UL &lt;&lt; (5 * 2));


/*
 * --------------------------------------------------------
 * Step 3. PA6 β†’ Alternate Function
 * --------------------------------------------------------
 *
 * PA6:
 *
 *     SPI1_MISO
 *
 * MODER<span>[13:12]</span> = 10
 */
GPIOA-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (6 * 2));
GPIOA-&gt;MODER |=  (0x2UL &lt;&lt; (6 * 2));


/*
 * --------------------------------------------------------
 * Step 4. PA7 β†’ Alternate Function
 * --------------------------------------------------------
 *
 * PA7:
 *
 *     SPI1_MOSI
 *
 * MODER<span>[15:14]</span> = 10
 */
GPIOA-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (7 * 2));
GPIOA-&gt;MODER |=  (0x2UL &lt;&lt; (7 * 2));


/*
 * --------------------------------------------------------
 * Step 5. PA9 β†’ General Purpose Output
 * --------------------------------------------------------
 *
 * PA9 is NOT using hardware NSS.
 *
 * It is manually controlled as the ADXL345
 * Chip Select signal.
 *
 * MODER<span>[19:18]</span> = 01
 */
GPIOA-&gt;MODER &amp;= ~(0x3UL &lt;&lt; (9 * 2));
GPIOA-&gt;MODER |=  (0x1UL &lt;&lt; (9 * 2));


/*
 * --------------------------------------------------------
 * Step 6. PA5 β†’ AF5
 * --------------------------------------------------------
 *
 * PA5 is in pin range 0~7,
 * therefore AFR<span>[0]</span> is used.
 *
 * Each pin uses 4 bits.
 */
GPIOA-&gt;AFR<span>[0]</span> &amp;= ~(0xFUL &lt;&lt; (5 * 4));
GPIOA-&gt;AFR<span>[0]</span> |=  (0x5UL &lt;&lt; (5 * 4));


/*
 * --------------------------------------------------------
 * Step 7. PA6 β†’ AF5
 * --------------------------------------------------------
 */
GPIOA-&gt;AFR<span>[0]</span> &amp;= ~(0xFUL &lt;&lt; (6 * 4));
GPIOA-&gt;AFR<span>[0]</span> |=  (0x5UL &lt;&lt; (6 * 4));


/*
 * --------------------------------------------------------
 * Step 8. PA7 β†’ AF5
 * --------------------------------------------------------
 */
GPIOA-&gt;AFR<span>[0]</span> &amp;= ~(0xFUL &lt;&lt; (7 * 4));
GPIOA-&gt;AFR<span>[0]</span> |=  (0x5UL &lt;&lt; (7 * 4));

}

/*

  • ============================================================
  • SPI1 Configuration
  • ============================================================ */

void spi1_config(void) { /* * -------------------------------------------------------- * Step 1. Enable SPI1 Clock * -------------------------------------------------------- * * SPI1 belongs to APB2. */ RCC->APB2ENR |= SPI1EN;

/*
 * --------------------------------------------------------
 * Step 2. Configure SPI Clock Divider
 * --------------------------------------------------------
 *
 * CR1 BR<span>[2:0]</span>
 *
 * Current setting:
 *
 *     001
 *
 * β†’ fPCLK / 4
 */

/*
 * BR0 = 1
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 3);

/*
 * BR1 = 0
 */
SPI1-&gt;CR1 &amp;= ~(1U &lt;&lt; 4);

/*
 * BR2 = 0
 */
SPI1-&gt;CR1 &amp;= ~(1U &lt;&lt; 5);


/*
 * --------------------------------------------------------
 * Step 3. Configure SPI Mode 3
 * --------------------------------------------------------
 *
 * ADXL345 configuration used in this practice:
 *
 *     CPOL = 1
 *     CPHA = 1
 *
 * SPI Mode 3
 */

/*
 * CPHA = 1
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 0);

/*
 * CPOL = 1
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 1);


/*
 * --------------------------------------------------------
 * Step 4. Full-Duplex Mode
 * --------------------------------------------------------
 *
 * BIDIMODE = 0
 *
 * Separate MOSI and MISO lines are used.
 */
SPI1-&gt;CR1 &amp;= ~(1U &lt;&lt; 10);


/*
 * --------------------------------------------------------
 * Step 5. MSB First
 * --------------------------------------------------------
 *
 * LSBFIRST = 0
 *
 * Most Significant Bit is transmitted first.
 */
SPI1-&gt;CR1 &amp;= ~(1U &lt;&lt; 7);


/*
 * --------------------------------------------------------
 * Step 6. Master Mode
 * --------------------------------------------------------
 *
 * MSTR = 1
 *
 * STM32 generates SCK.
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 2);


/*
 * --------------------------------------------------------
 * Step 7. 8-bit Data Frame
 * --------------------------------------------------------
 *
 * DFF = 0
 *
 * Each SPI transfer uses 8-bit data.
 */
SPI1-&gt;CR1 &amp;= ~(1U &lt;&lt; 11);


/*
 * --------------------------------------------------------
 * Step 8. Software Slave Management
 * --------------------------------------------------------
 *
 * SSM = 1
 *
 * Hardware NSS management is disabled.
 *
 * PA9 GPIO is used as CS instead.
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 8);


/*
 * --------------------------------------------------------
 * Step 9. Internal Slave Select
 * --------------------------------------------------------
 *
 * SSI = 1
 *
 * Required with software slave management
 * so the master does not enter mode-fault state.
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 9);


/*
 * --------------------------------------------------------
 * Step 10. Enable SPI1
 * --------------------------------------------------------
 *
 * SPE = 1
 */
SPI1-&gt;CR1 |= (1U &lt;&lt; 6);

}

/*

  • ============================================================
  • SPI Transmit
  • ============================================================ */

void spi1_transmit(uint8_t *data, uint32_t size) { uint32_t i = 0; uint8_t temp;

/*
 * Send every byte in the TX buffer.
 */
while (i &lt; size)
{
    /*
     * Wait until transmit buffer becomes empty.
     *
     * TXE = 1
     *
     * means SPI1-&gt;DR can accept new data.
     */
    while (!(SPI1-&gt;SR &amp; SR_TXE))
    {
    }


    /*
     * Write one byte to Data Register.
     *
     * SPI hardware now shifts this byte
     * out through MOSI.
     */
    SPI1-&gt;DR = data<span>[i]</span>;


    /*
     * Move to next byte.
     */
    i++;
}


/*
 * Wait until the final value has moved
 * out of the transmit buffer.
 */
while (!(SPI1-&gt;SR &amp; SR_TXE))
{
}


/*
 * TXE only tells us the transmit buffer is empty.
 *
 * The SPI shift register may still be transferring
 * the final bits on the physical bus.
 *
 * Therefore also wait until:
 *
 *     BSY = 0
 */
while (SPI1-&gt;SR &amp; SR_BSY)
{
}


/*
 * --------------------------------------------------------
 * Clear possible OVR condition
 * --------------------------------------------------------
 *
 * SPI is full-duplex.
 *
 * Even while transmitting, received data may
 * accumulate in the receive side.
 *
 * The required clear sequence is:
 *
 *     Read DR
 *       ↓
 *     Read SR
 *
 * The actual value is not needed.
 */
temp = SPI1-&gt;DR;
temp = SPI1-&gt;SR;

/*
 * Prevent compiler warning:
 *
 * variable intentionally read only for
 * hardware side effect.
 */
(void)temp;

}

/*

  • ============================================================
  • SPI Receive
  • ============================================================ */

void spi1_receive(uint8_t data, uint32_t size) { while (size) { / * ---------------------------------------------------- * Generate SPI Clock * ---------------------------------------------------- * * SPI slave cannot send data by itself. * * SCK is generated only by the master. * * Therefore even when we only want to receive, * the master must transmit a dummy byte. * * The actual dummy data value is irrelevant here. */ SPI1->DR = 0;

    /*
     * Wait until a byte has been received.
     *
     * RXNE:
     *
     *     0 β†’ no received byte
     *     1 β†’ received byte available
     */
    while (!(SPI1-&gt;SR &amp; SR_RXNE))
    {
    }


    /*
     * Read received byte from Data Register
     * and store it in the caller's buffer.
     */
    *data++ = (uint8_t)SPI1-&gt;DR;


    /*
     * One byte completed.
     */
    size--;
}

}

/*

  • ============================================================
  • Chip Select Control
  • ============================================================ */

void cs_enable(void) { /* * ADXL345 chip select is active LOW. * * PA9 = 0 * * β†’ Sensor selected * β†’ SPI transaction begins */ GPIOA->ODR &= ~(1U << 9); }

void cs_disable(void) { /* * PA9 = 1 * * β†’ Sensor deselected * β†’ SPI transaction ends */ GPIOA->ODR |= (1U << 9); }


8. SPI Configuration Flow

spi_gpio_init()
β”‚
β”œβ”€β”€ GPIOA Clock ON
β”œβ”€β”€ PA5 β†’ AF Mode
β”œβ”€β”€ PA6 β†’ AF Mode
β”œβ”€β”€ PA7 β†’ AF Mode
β”œβ”€β”€ PA9 β†’ Output
└── PA5/6/7 β†’ AF5

spi1_config() β”‚ β”œβ”€β”€ SPI1 Clock ON β”œβ”€β”€ BR = /4 β”œβ”€β”€ CPOL = 1 β”œβ”€β”€ CPHA = 1 β”œβ”€β”€ Full Duplex β”œβ”€β”€ MSB First β”œβ”€β”€ Master β”œβ”€β”€ 8-bit β”œβ”€β”€ SSM = 1 β”œβ”€β”€ SSI = 1 └── SPE = 1


9. SPI Transmit Flow

TX Buffer
↓
Wait TXE
↓
Write SPI1->DR
↓
Shift Register
↓
MOSI
↓
ADXL345

λ§ˆμ§€λ§‰ byteκΉŒμ§€ λ„£μ—ˆλ‹€κ³  λ°”λ‘œ transaction이 λλ‚œ 것은 μ•„λ‹ˆλ‹€.

TXE = 1

은 transmit bufferκ°€ λΉ„μ—ˆλ‹€λŠ” 뜻이고:

BSY = 0

κΉŒμ§€ 확인해야 μ‹€μ œ bus transfer μ™„λ£Œλ₯Ό 확인할 수 μžˆλ‹€.

κ·Έλž˜μ„œ:

while (!(SPI1->SR & SR_TXE))
{
}

while (SPI1->SR & SR_BSY) { }

두 μƒνƒœλ₯Ό λͺ¨λ‘ ν™•μΈν•œλ‹€.


10. Why Dummy Write?

SPIμ—μ„œλŠ” masterκ°€ SCKλ₯Ό λ§Œλ“ λ‹€.

λ”°λΌμ„œ slave data만 λ°›κ³  싢어도:

Master
↓
Generate Clock
↓
Slave shifts MISO data

과정이 ν•„μš”ν•˜λ‹€.

μ½”λ“œ:

SPI1->DR = 0;

의 0은 의미 μžˆλŠ” command dataκ°€ μ•„λ‹ˆλΌ clock을 λ°œμƒμ‹œν‚€κΈ° μœ„ν•œ dummy transmit이닀.

Dummy TX
↓
SCK generated
↓
Slave shifts data
↓
RXNE = 1
↓
Read DR

11. Full-Duplex

SPIλŠ” 기본적으둜 transmitκ³Ό receiveκ°€ λ™μ‹œμ— μ§„ν–‰λœλ‹€.

SCK edge
β”‚
β”œβ”€β”€ MOSI bit β†’ Slave
β”‚
└── MISO bit β†’ Master

즉:

Transmit only

처럼 μ½”λ“œλ₯Ό μž‘μ„±ν•΄λ„ hardwareμ—μ„œλŠ” receive pathκ°€ 같이 λ™μž‘ν•  수 μžˆλ‹€.

κ·Έλž˜μ„œ μ‚¬μš©ν•˜μ§€ μ•Šμ€ receive dataλ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠμœΌλ©΄:

OVR
Overrun

μƒνƒœκ°€ λ°œμƒν•  수 μžˆλ‹€.


12. OVR Clear

원본 μ½”λ“œ:

temp = SPI1->DR;
temp = SPI1->SR;
(void)temp;

이 뢀뢄은 값을 κ³„μ‚°ν•˜κΈ° μœ„ν•΄ μ‘΄μž¬ν•˜λŠ” 것이 μ•„λ‹ˆλ‹€.

Read DR
↓
Read SR
↓
OVR clear

λΌλŠ” hardware-defined side effectλ₯Ό μ‚¬μš©ν•œλ‹€.

즉 bare-metalμ—μ„œλŠ”:

registerλ₯Ό μ½λŠ” ν–‰μœ„ μžμ²΄κ°€ hardware stateλ₯Ό λ³€κ²½ν•  수 μžˆλ‹€.

λŠ” 점이 μ€‘μš”ν•˜λ‹€.


13. Chip Select

SPI transaction의 κΈ°λ³Έ 경계:

CS HIGH
↓
CS LOW
↓
Command
↓
Data
↓
CS HIGH

μ½”λ“œμ—μ„œλŠ”:

cs_enable();

/* SPI communication */

cs_disable();

ν˜•νƒœλ‘œ μ‚¬μš©ν•œλ‹€.


14. Why GPIO CS?

이번 μ½”λ“œλŠ” hardware NSSκ°€ μ•„λ‹ˆλΌ:

PA9 GPIO

λ₯Ό CS둜 μ‚¬μš©ν•œλ‹€.

그리고 SPI λ‚΄λΆ€μ—μ„œλŠ”:

SSM = 1
SSI = 1

둜 software slave managementλ₯Ό μ‚¬μš©ν•œλ‹€.

μ΄λ ‡κ²Œ ν•˜λ©΄ applicationμ—μ„œ:

Sensor A CS
Sensor B CS
Sensor C CS

처럼 μ—¬λŸ¬ slaveλ₯Ό 각 GPIO둜 μ œμ–΄ν•˜κΈ° 쉽닀.


15. ADXL345 Verification Target

⚠️ λ‹€μŒ λ‚΄μš©μ€ 아직 μ‹€μ œ μΈ‘μ • κ²°κ³Όκ°€ μ•„λ‹ˆλΌ μž¬μ‹€μŠ΅ μ‹œ 확인해야 ν•  λͺ©ν‘œκ°’이닀.

첫 번째 검증은 acceleration data보닀:

DEVID

λ₯Ό λ¨Όμ € μ½λŠ” 것이 μ’‹λ‹€.

Expected:

ADXL345 DEVID

0xE5

즉:

STM32 SPI μ„€μ •
+
λ°°μ„ 
+
CS
+
SPI Mode
+
Sensor 응닡

이 λͺ¨λ‘ 정상인지 κ°€μž₯ κ°„λ‹¨ν•˜κ²Œ 확인할 수 μžˆλ‹€.


16. Application Layer Status

μ›λž˜ Chapter λͺ©ν‘œμ—λŠ”:

ADXL345 DEVID Read
X/Y/Z Acceleration Read
UART Output

κΉŒμ§€ ν¬ν•¨λ˜μ–΄ μžˆλ‹€.

ν•˜μ§€λ§Œ ν˜„μž¬ 보쑴된 sourceμ—μ„œ 전체 λ‚΄μš©μ„ 확인할 수 μžˆλŠ” 것은:

spi.h
spi.c

κΉŒμ§€λ‹€.

λ”°λΌμ„œ ν˜„μž¬ Wikiμ—μ„œλŠ” application codeλ₯Ό μΆ”μΈ‘ν•΄μ„œ μ›λ³Έμ²˜λŸΌ λ§Œλ“€μ§€ μ•ŠλŠ”λ‹€.

μž¬μ‹€μŠ΅ ν›„:

adxl345.h
adxl345.c
main.c

λ₯Ό μ‹€μ œ μ‹€ν–‰ν–ˆλ˜ source κ·ΈλŒ€λ‘œ μΆ”κ°€ν•œλ‹€.


17. Build Result

ν˜„μž¬ ν™•μΈλœ κ²°κ³Ό:

Compile : PASS
Link    : PASS
Hardware: NOT VERIFIED

arm-none-eabi-size:

text = 2328 bytes
data = 0 bytes
bss  = 2560 bytes

κ²½κ³  없이 buildκ°€ μ™„λ£Œλ˜μ—ˆλ‹€.


18. Hardware Re-Test Plan

ADXL345 module 확보 ν›„ λ‹€μŒ μˆœμ„œλ‘œ κ²€μ¦ν•œλ‹€.

Step 1 β€” Wiring

STM32F439ZI       ADXL345

PA5 SCK β†’ SCLK PA6 MISO ← SDO PA7 MOSI β†’ SDA/SDI PA9 CS β†’ CS

3.3V β†’ VCC GND β†’ GND

μ‹€μ œ module pin labelκ³Ό 전원 사양은 μ‚¬μš© λͺ¨λ“ˆμ˜ datasheet/board schematic을 λ‹€μ‹œ ν™•μΈν•œλ‹€.


Step 2 β€” Debug GPIO

확인:

PA5 MODER β†’ AF
PA6 MODER β†’ AF
PA7 MODER β†’ AF

PA5 AF β†’ 5 PA6 AF β†’ 5 PA7 AF β†’ 5

PA9 MODER β†’ Output


Step 3 β€” Debug SPI1

확인:

SPI1->CR1

Expected:

MSTR = 1
CPOL = 1
CPHA = 1
DFF  = 0
SSM  = 1
SSI  = 1
SPE  = 1

Step 4 β€” Check CS

Idle:

PA9 = HIGH

Transaction:

PA9 = LOW

μ™„λ£Œ:

PA9 = HIGH

Step 5 β€” Read DEVID

First hardware target:

DEVID == 0xE5

κ²°κ³Όκ°€ λ‹€λ₯΄λ©΄ λ¨Όμ €:

Wiring
SPI Mode
CS polarity
MOSI/MISO swap
AF setting
Clock rate

을 ν™•μΈν•œλ‹€.


Step 6 β€” Read X/Y/Z

DEVIDκ°€ 정상일 λ•Œ κ·Έλ‹€μŒ:

DATAX
DATAY
DATAZ

multi-byte readλ₯Ό κ²€μ¦ν•œλ‹€.


19. Screenshots to Add

ν˜„μž¬ 넣을 수 μžˆλŠ” 것:

images/ch12_build.png
images/ch12_size.png

μ‹€κΈ° ν›„ μΆ”κ°€:

images/ch12_wiring.jpg
images/ch12_spi_registers.png
images/ch12_devid.png
images/ch12_logic_analyzer.png
images/ch12_xyz_uart.png

특히 κ°€μž₯ μ€‘μš”ν•œ μ‹€κΈ° 증거:

DEVID = 0xE5

와 κ°€λŠ₯ν•˜λ©΄:

CS
SCK
MOSI
MISO

logic analyzer waveform이닀.


20. Key Points

SPI1 Master
β”‚
β”œβ”€β”€ PA5 β†’ SCK
β”œβ”€β”€ PA6 ← MISO
β”œβ”€β”€ PA7 β†’ MOSI
└── PA9 β†’ CS
↓
ADXL345
  • SPIλŠ” masterκ°€ SCKλ₯Ό μƒμ„±ν•˜λŠ” synchronous serial protocol이닀.

  • SPI1의 PA5/PA6/PA7은 AF5λ₯Ό μ‚¬μš©ν•œλ‹€.

  • PA9λŠ” software-controlled CS둜 μ‚¬μš©ν•œλ‹€.

  • MSTR=1둜 Master modeλ₯Ό μ„€μ •ν•œλ‹€.

  • 이번 μ‹€μŠ΅μ€ CPOL=1, CPHA=1인 Mode 3λ₯Ό μ‚¬μš©ν•œλ‹€.

  • SSM=1, SSI=1둜 software slave managementλ₯Ό μ‚¬μš©ν•œλ‹€.

  • TXEλŠ” transmit buffer emptyλ₯Ό μ˜λ―Έν•œλ‹€.

  • RXNEλŠ” received dataκ°€ μžˆλ‹€λŠ” λœ»μ΄λ‹€.

  • BSYλŠ” μ‹€μ œ SPI transferκ°€ μ§„ν–‰ μ€‘μž„μ„ λ‚˜νƒ€λ‚Έλ‹€.

  • SPI receiveμ—μ„œλ„ masterκ°€ clock을 λ§Œλ“€κΈ° μœ„ν•΄ dummy byteλ₯Ό transmitν•΄μ•Ό ν•œλ‹€.

  • OVR clearμ—λŠ” DR read β†’ SR read sequenceκ°€ μ‚¬μš©λœλ‹€.

  • ν˜„μž¬ μƒνƒœλŠ” build/link PASS, hardware verification pending이닀.

  • 첫 μ‹€κΈ° 확인 λͺ©ν‘œλŠ” **ADXL345 DEVID = 0xE5**λ‹€.

⚠️ **GitHub.com Fallback** ⚠️