Bare metal C Ch12 SPI - Hongjayeong-02/Basic_SoC_implementation GitHub Wiki
ν μ€ μμ½: SPI1μ Masterλ‘ μ€μ νκ³ ADXL345μ ν΅μ νλ driverλ₯Ό ꡬννλ€. νμ¬ κΈ°λ‘μ μΌμ λͺ¨λμ΄ μμ΄ μ€μ μΌμ μλ΅μ λ―Έκ²μ¦ μνλ€.
SPIλ Masterκ° SCK clockμ μ§μ μμ±νλ λκΈ°μ serial protocolμ΄λ€.
SCK : clock
MOSI : Master β Slave
MISO : Slave β Master
CS : Slave select
Notion κΈ°λ‘ κΈ°μ€ μμμ F439ZIμμ λμΌν λ§€νμ μ¬μ©νλ€.
κΈ°λ₯ | Pin -- | -- SPI1 SCK | PA5 / AF5 SPI1 MISO | PA6 / AF5 SPI1 MOSI | PA7 / AF5 ADXL345 CS | PA9 GPIOADXL345μ©μΌλ‘ μ½λμμλ CPOL=1, CPHA=1μ μ€μ νλ€.
SPI GPIO init
β
βΌ
SPI1 Master config
β
βΌ
ADXL345 DATA_FORMAT / POWER_CTL write
β
βΌ
CS Low
β
βΌ
Register address transmit
β
βΌ
Dummy TXλ‘ clock μμ± + RX
β
βΌ
CS High
β
βΌ
X/Y/Z μ‘°ν© β UART μΆλ ₯
Ch12_spi/
βββ Inc/spi.h
βββ Src/spi.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 SPI_H_
#define SPI_H_
#include <stdint.h>
#include "stm32f4xx.h"
void spi_gpio_init(void);
void spi1_config(void);
void spi1_transmit(uint8_t *data, uint32_t size);
void spi1_receive(uint8_t *data, uint32_t size);
void cs_enable(void);
void cs_disable(void);
#endif
#include "spi.h"
#define GPIOAEN (1U << 0)
#define SPI1EN (1U << 12)
#define SR_RXNE (1U << 0)
#define SR_TXE (1U << 1)
#define SR_BSY (1U << 7)
void spi_gpio_init(void)
{
RCC->AHB1ENR |= GPIOAEN;
GPIOA->MODER &= ~(0x3UL << (5 * 2));
GPIOA->MODER |= (0x2UL << (5 * 2));
GPIOA->MODER &= ~(0x3UL << (6 * 2));
GPIOA->MODER |= (0x2UL << (6 * 2));
GPIOA->MODER &= ~(0x3UL << (7 * 2));
GPIOA->MODER |= (0x2UL << (7 * 2));
GPIOA->MODER &= ~(0x3UL << (9 * 2));
GPIOA->MODER |= (0x1UL << (9 * 2)); /* PA9 = CS, λ²μ©μΆλ ₯ */
GPIOA->AFR[0] &= ~(0xFUL << (5 * 4));
GPIOA->AFR[0] |= (0x5UL << (5 * 4));
GPIOA->AFR[0] &= ~(0xFUL << (6 * 4));
GPIOA->AFR[0] |= (0x5UL << (6 * 4));
GPIOA->AFR[0] &= ~(0xFUL << (7 * 4));
GPIOA->AFR[0] |= (0x5UL << (7 * 4));
}
void spi1_config(void)
{
RCC->APB2ENR |= SPI1EN;
SPI1->CR1 |= (1U << 3);
SPI1->CR1 &= ~(1U << 4);
SPI1->CR1 &= ~(1U << 5);
SPI1->CR1 |= (1U << 0); /* CPHA */
SPI1->CR1 |= (1U << 1); /* CPOL */
SPI1->CR1 &= ~(1U << 10);
SPI1->CR1 &= ~(1U << 7);
SPI1->CR1 |= (1U << 2);
SPI1->CR1 &= ~(1U << 11);
SPI1->CR1 |= (1U << 8);
SPI1->CR1 |= (1U << 9);
SPI1->CR1 |= (1U << 6);
}
void spi1_transmit(uint8_t *data, uint32_t size)
{
uint32_t i = 0;
uint8_t temp;
while (i < size)
{
while (!(SPI1->SR & SR_TXE)) { }
SPI1->DR = data[i];
i++;
}
while (!(SPI1->SR & SR_TXE)) { }
while ((SPI1->SR & SR_BSY)) { }
temp = SPI1->DR;
temp = SPI1->SR;
(void)temp;
}
void spi1_receive(uint8_t *data, uint32_t size)
{
while (size)
{
SPI1->DR = 0;
while (!(SPI1->SR & SR_RXNE)) { }
*data++ = (uint8_t)SPI1->DR;
size--;
}
}
void cs_enable(void) { GPIOA->ODR &= ~(1U << 9); }
void cs_disable(void) { GPIOA->ODR |= (1U << 9); }
#ifndef ADXL345_H_
#define ADXL345_H_
#include <stdint.h>
#include "spi.h"
#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
#define ADXL345_MULTI_BYTE_ENABLE 0x40U
#define ADXL345_READ_OPERATION 0x80U
void adxl_init(void);
void adxl_read(uint8_t address, uint8_t *rxdata);
void adxl_write(uint8_t address, uint8_t value);
#endif
#include "adxl345.h"
void adxl_read(uint8_t address, uint8_t *rxdata)
{
address |= ADXL345_READ_OPERATION;
address |= ADXL345_MULTI_BYTE_ENABLE;
cs_enable();
spi1_transmit(&address, 1);
spi1_receive(rxdata, 6);
cs_disable();
}
void adxl_write(uint8_t address, uint8_t value)
{
uint8_t data[2];
data[0] = address | ADXL345_MULTI_BYTE_ENABLE;
data[1] = value;
cs_enable();
spi1_transmit(data, 2);
cs_disable();
}
void adxl_init(void)
{
spi_gpio_init();
spi1_config();
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);
}
#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"
#include "uart.h"
#include "systick.h"
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 DEVID (expect 0xE5): 0x%02X\r\n", devid);
while (1)
{
adxl_read(ADXL345_REG_DATA_START, 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 κΈ°λ‘:
make/build λ° link νμΈADXL345 sensor module λΆμ¬
μ€μ
DEVID = 0xE5λ° X/Y/Z κ°μ λ―Έκ²μ¦
text = 2328 bytes
data = 0 bytes
bss = 2560 bytes
warning μμ
μ²¨λΆ ZIPμ adxl_read()λ μ£Όμμ μκ΄μμ΄ νμ 6 byteλ₯Ό μμ νλ€.
spi1_receive(rxdata, 6);
κ·Έλ°λ° main.cμμλ DEVID νμΈ μ:
uint8_t devid;
adxl_read(ADXL345_REG_DEVID, &devid);
μ²λΌ 1 byte λ³μμ μ£Όμλ₯Ό λκΈ΄λ€. νμ¬ μΌμ μ€κΈ°κ° λ―Έκ²μ¦ μνμ΄λ―λ‘, μ€μ μΌμ μ°κ²° μ μ λ¨μΌ-byte readμ 6-byte burst readλ₯Ό λΆλ¦¬ν μ§ νμΈν νμκ° μλ€.
μ¬κΈ°μλ 첨λΆλ μ€μ μ½λλ₯Ό μμ νμ§ μκ³ κ·Έλλ‘ κΈ°λ‘νλ€.
SPIλ Masterκ° clockμ μμ±νλ€.
μμ λ§ ν λλ clock μμ±μ μν΄ dummy transmitμ΄ νμνλ€.
νμ¬ ADXL345 sensor hardware κ²μ¦μ λ¨μ μλ€.