Chapter 12 - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
β οΈ Hardware Not Verifiedμ΄ Chapterλ
makeλ₯Ό μ΄μ©ν Compile / LinkκΉμ§ νμΈνλ€.
ADXL345 sensor moduleμ΄ μμ΄μ μ€μ SPI waveform,DEVID = 0xE5, X/Y/Z acceleration κ°μ μμ§ κ²μ¦νμ§ μμλ€.
STM32F439ZIμ SPI1μ Master modeλ‘ μ€μ νκ³ ADXL345 accelerometerμ SPI ν΅μ νκΈ° μν low-level driverλ₯Ό ꡬννλ€.
STM32F439ZI
β
β SPI1
β
βββ PA5 β SCK
βββ PA6 β MISO
βββ PA7 β MOSI
βββ PA9 β CS
β
βΌ
ADXL345SPI μ€μ :
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 = 1SPIλ 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
μ΄λ² μ€μ΅:
| 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μΌλ‘ μ§μ μ μ΄νλ€.
μ΄λ² μ½λμμ μ¬μ©νλ 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
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 CommandMulti-byte readλΌλ©΄:
Register Address
|
0x80
|
0x40ννκ° λλ€.
νμ¬ λ³΄μ‘΄λ μ€μ΅ μλ³Έμμ νμΈλλ νμΌ:
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λ₯Ό μ€μ ν μ€νΈ μ½λ κΈ°μ€μΌλ‘ μΆκ°νλ€.
#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
#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->MODER &= ~(0x3UL << (5 * 2));
GPIOA->MODER |= (0x2UL << (5 * 2));
/*
* --------------------------------------------------------
* Step 3. PA6 β Alternate Function
* --------------------------------------------------------
*
* PA6:
*
* SPI1_MISO
*
* MODER<span>[13:12]</span> = 10
*/
GPIOA->MODER &= ~(0x3UL << (6 * 2));
GPIOA->MODER |= (0x2UL << (6 * 2));
/*
* --------------------------------------------------------
* Step 4. PA7 β Alternate Function
* --------------------------------------------------------
*
* PA7:
*
* SPI1_MOSI
*
* MODER<span>[15:14]</span> = 10
*/
GPIOA->MODER &= ~(0x3UL << (7 * 2));
GPIOA->MODER |= (0x2UL << (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->MODER &= ~(0x3UL << (9 * 2));
GPIOA->MODER |= (0x1UL << (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->AFR<span>[0]</span> &= ~(0xFUL << (5 * 4));
GPIOA->AFR<span>[0]</span> |= (0x5UL << (5 * 4));
/*
* --------------------------------------------------------
* Step 7. PA6 β AF5
* --------------------------------------------------------
*/
GPIOA->AFR<span>[0]</span> &= ~(0xFUL << (6 * 4));
GPIOA->AFR<span>[0]</span> |= (0x5UL << (6 * 4));
/*
* --------------------------------------------------------
* Step 8. PA7 β AF5
* --------------------------------------------------------
*/
GPIOA->AFR<span>[0]</span> &= ~(0xFUL << (7 * 4));
GPIOA->AFR<span>[0]</span> |= (0x5UL << (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->CR1 |= (1U << 3);
/*
* BR1 = 0
*/
SPI1->CR1 &= ~(1U << 4);
/*
* BR2 = 0
*/
SPI1->CR1 &= ~(1U << 5);
/*
* --------------------------------------------------------
* Step 3. Configure SPI Mode 3
* --------------------------------------------------------
*
* ADXL345 configuration used in this practice:
*
* CPOL = 1
* CPHA = 1
*
* SPI Mode 3
*/
/*
* CPHA = 1
*/
SPI1->CR1 |= (1U << 0);
/*
* CPOL = 1
*/
SPI1->CR1 |= (1U << 1);
/*
* --------------------------------------------------------
* Step 4. Full-Duplex Mode
* --------------------------------------------------------
*
* BIDIMODE = 0
*
* Separate MOSI and MISO lines are used.
*/
SPI1->CR1 &= ~(1U << 10);
/*
* --------------------------------------------------------
* Step 5. MSB First
* --------------------------------------------------------
*
* LSBFIRST = 0
*
* Most Significant Bit is transmitted first.
*/
SPI1->CR1 &= ~(1U << 7);
/*
* --------------------------------------------------------
* Step 6. Master Mode
* --------------------------------------------------------
*
* MSTR = 1
*
* STM32 generates SCK.
*/
SPI1->CR1 |= (1U << 2);
/*
* --------------------------------------------------------
* Step 7. 8-bit Data Frame
* --------------------------------------------------------
*
* DFF = 0
*
* Each SPI transfer uses 8-bit data.
*/
SPI1->CR1 &= ~(1U << 11);
/*
* --------------------------------------------------------
* Step 8. Software Slave Management
* --------------------------------------------------------
*
* SSM = 1
*
* Hardware NSS management is disabled.
*
* PA9 GPIO is used as CS instead.
*/
SPI1->CR1 |= (1U << 8);
/*
* --------------------------------------------------------
* Step 9. Internal Slave Select
* --------------------------------------------------------
*
* SSI = 1
*
* Required with software slave management
* so the master does not enter mode-fault state.
*/
SPI1->CR1 |= (1U << 9);
/*
* --------------------------------------------------------
* Step 10. Enable SPI1
* --------------------------------------------------------
*
* SPE = 1
*/
SPI1->CR1 |= (1U << 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 < size)
{
/*
* Wait until transmit buffer becomes empty.
*
* TXE = 1
*
* means SPI1->DR can accept new data.
*/
while (!(SPI1->SR & SR_TXE))
{
}
/*
* Write one byte to Data Register.
*
* SPI hardware now shifts this byte
* out through MOSI.
*/
SPI1->DR = data<span>[i]</span>;
/*
* Move to next byte.
*/
i++;
}
/*
* Wait until the final value has moved
* out of the transmit buffer.
*/
while (!(SPI1->SR & 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->SR & 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->DR;
temp = SPI1->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->SR & SR_RXNE))
{
}
/*
* Read received byte from Data Register
* and store it in the caller's buffer.
*/
*data++ = (uint8_t)SPI1->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);
}
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
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)
{
}
λ μνλ₯Ό λͺ¨λ νμΈνλ€.
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 DRSPIλ κΈ°λ³Έμ μΌλ‘ transmitκ³Ό receiveκ° λμμ μ§νλλ€.
SCK edge
β
βββ MOSI bit β Slave
β
βββ MISO bit β Masterμ¦:
Transmit onlyμ²λΌ μ½λλ₯Ό μμ±ν΄λ hardwareμμλ receive pathκ° κ°μ΄ λμν μ μλ€.
κ·Έλμ μ¬μ©νμ§ μμ receive dataλ₯Ό μ²λ¦¬νμ§ μμΌλ©΄:
OVR
Overrunμνκ° λ°μν μ μλ€.
μλ³Έ μ½λ:
temp = SPI1->DR;
temp = SPI1->SR;
(void)temp;μ΄ λΆλΆμ κ°μ κ³μ°νκΈ° μν΄ μ‘΄μ¬νλ κ²μ΄ μλλ€.
Read DR
β
Read SR
β
OVR clearλΌλ hardware-defined side effectλ₯Ό μ¬μ©νλ€.
μ¦ bare-metalμμλ:
registerλ₯Ό μ½λ νμ μμ²΄κ° hardware stateλ₯Ό λ³κ²½ν μ μλ€.
λ μ μ΄ μ€μνλ€.
SPI transactionμ κΈ°λ³Έ κ²½κ³:
CS HIGH
β
CS LOW
β
Command
β
Data
β
CS HIGHμ½λμμλ:
cs_enable();
/* SPI communication */
cs_disable();
ννλ‘ μ¬μ©νλ€.
μ΄λ² μ½λλ 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λ‘ μ μ΄νκΈ° μ½λ€.
첫 λ²μ§Έ κ²μ¦μ acceleration data보λ€:
DEVIDλ₯Ό λ¨Όμ μ½λ κ²μ΄ μ’λ€.
Expected:
ADXL345 DEVID
0xE5
μ¦:
STM32 SPI μ€μ
+
λ°°μ
+
CS
+
SPI Mode
+
Sensor μλ΅μ΄ λͺ¨λ μ μμΈμ§ κ°μ₯ κ°λ¨νκ² νμΈν μ μλ€.
μλ 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 κ·Έλλ‘ μΆκ°νλ€.
νμ¬ νμΈλ κ²°κ³Ό:
Compile : PASS
Link : PASS
Hardware: NOT VERIFIEDarm-none-eabi-size:
text = 2328 bytes
data = 0 bytes
bss = 2560 bytesκ²½κ³ μμ΄ buildκ° μλ£λμλ€.
ADXL345 module ν보 ν λ€μ μμλ‘ κ²μ¦νλ€.
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μ λ€μ νμΈνλ€.
νμΈ:
PA5 MODER β AF
PA6 MODER β AF
PA7 MODER β AF
PA5 AF β 5
PA6 AF β 5
PA7 AF β 5
PA9 MODER β Output
νμΈ:
SPI1->CR1Expected:
MSTR = 1
CPOL = 1
CPHA = 1
DFF = 0
SSM = 1
SSI = 1
SPE = 1Idle:
PA9 = HIGHTransaction:
PA9 = LOWμλ£:
PA9 = HIGHFirst hardware target:
DEVID == 0xE5κ²°κ³Όκ° λ€λ₯΄λ©΄ λ¨Όμ :
Wiring
SPI Mode
CS polarity
MOSI/MISO swap
AF setting
Clock rateμ νμΈνλ€.
DEVIDκ° μ μμΌ λ κ·Έλ€μ:
DATAX
DATAY
DATAZmulti-byte readλ₯Ό κ²μ¦νλ€.
νμ¬ λ£μ μ μλ κ²:
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
MISOlogic analyzer waveformμ΄λ€.
SPI1 Master
β
βββ PA5 β SCK
βββ PA6 β MISO
βββ PA7 β MOSI
βββ PA9 β CS
β
ADXL345SPIλ 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 readsequenceκ° μ¬μ©λλ€.νμ¬ μνλ build/link PASS, hardware verification pendingμ΄λ€.
첫 μ€κΈ° νμΈ λͺ©νλ **ADXL345 DEVID =
0xE5**λ€.