1. STM32G474 I2C 및 SPI 개요
STM32G474는 STMicroelectronics의 STM32G4 시리즈에 속하는 고성능 32비트 ARM Cortex-M4 마이크로컨트롤러로, 최대 170MHz로 동작하며 I2C와 SPI 통신 인터페이스를 지원합니다. I2C(Inter-Integrated Circuit)와 SPI(Serial Peripheral Interface)는 외부 장치(센서, 디스플레이 등)와 통신하기 위한 직렬 통신 프로토콜입니다. 이 문서에서는 STM32G474의 I2C와 SPI를 HAL API를 사용하여 설정하고 사용하는 방법을 설명합니다. 모든 예제 코드는 STM32CubeMX로 생성되며, STM32CubeIDE에서 실행 가능합니다. 코드에는 상세한 주석이 포함되어 있습니다.
I2C와 SPI의 주요 특징
I2C
- 양방향 2선 통신 (SDA: 데이터, SCL: 클럭).
- 다중 마스터 및 슬레이브 지원.
- 속도: 표준 모드(100 kHz), 고속 모드(400 kHz), 초고속 모드(1 MHz).
- STM32G474는 최대 4개의 I2C 인터페이스(I2C1~I2C4) 제공.
- 주소: 7비트 또는 10비트 주소 지원.
SPI
- 전이중 동기 통신 (MOSI, MISO, SCK, NSS).
- 마스터/슬레이브 모드 지원.
- 속도: 최대 85 MHz (STM32G474의 클럭 설정에 따라 달라짐).
- STM32G474는 최대 4개의 SPI 인터페이스(SPI1~SPI4) 제공.
- 데이터 프레임: 4~16비트.
HAL API는 복잡한 레지스터 설정을 추상화하여 I2C와 SPI 설정을 간소화합니다.
2. 주요 I2C 및 SPI HAL API 함수
2.1. I2C HAL API 함수
- *HAL_I2C_Init(I2C_HandleTypeDef hi2c)
- 설명: I2C 인터페이스 초기화.
- 매개변수: hi2c (I2C 핸들러 구조체).
- **HAL_I2C_Master_Transmit(I2C_HandleTypeDef hi2c, uint16_t DevAddress, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 마스터 모드로 데이터 전송.
- 매개변수: DevAddress (슬레이브 주소), pData (전송 데이터), Size (데이터 크기), Timeout (타임아웃).
- **HAL_I2C_Master_Receive(I2C_HandleTypeDef hi2c, uint16_t DevAddress, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 마스터 모드로 데이터 수신.
- **HAL_I2C_Mem_Write(I2C_HandleTypeDef hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 슬레이브 메모리에 데이터 쓰기.
- **HAL_I2C_Mem_Read(I2C_HandleTypeDef hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 슬레이브 메모리에서 데이터 읽기.
2.2. SPI HAL API 함수
- *HAL_SPI_Init(SPI_HandleTypeDef hspi)
- 설명: SPI 인터페이스 초기화.
- 매개변수: hspi (SPI 핸들러 구조체).
- **HAL_SPI_Transmit(SPI_HandleTypeDef hspi, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 마스터 모드로 데이터 전송.
- **HAL_SPI_Receive(SPI_HandleTypeDef hspi, uint8_t pData, uint16_t Size, uint32_t Timeout)
- 설명: 마스터 모드로 데이터 수신.
- **HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t pTxData, uint8_t pRxData, uint16_t Size, uint32_t Timeout)
- 설명: 전이중 모드로 데이터 송수신.
3. I2C 및 SPI 설정 절차
3.1. STM32CubeMX 설정 절차
- 프로젝트 생성:
- STM32CubeMX에서 "New Project"를 선택하고 STM32G474 선택 (예: LQFP64 패키지).
- 프로젝트 이름과 저장 경로 설정.
- 시스템 클럭 설정 (최대 170 MHz, HSE 사용):
- "System Core" > "RCC"에서 HSE를 "Crystal/Ceramic Resonator"로 설정 (8 MHz 가정).
- Clock Configuration 탭:
- HSE: 8 MHz.
- PLL 설정: PLLM = 1, PLLN = 85, PLLR = 2, PLLR Enable.
- SYSCLK = (8 MHz / 1) * 85 / 2 = 170 MHz.
- HCLK, PCLK1, PCLK2를 170 MHz로 설정 (Divider = 1).
- Flash Latency: 4 WS.
- I2C 설정 (예제 1):
- "Connectivity" > "I2C1" 활성화.
- Parameter Settings:
- I2C Speed Mode: Fast Mode (400 kHz).
- Duty Cycle: 2.
- Addressing Mode: 7-bit.
- GPIO 설정:
- I2C1_SCL (PB6), I2C1_SDA (PB7)를 "Alternate Function Open Drain"으로 설정.
- Pull-up 활성화.
- SPI 설정 (예제 2):
- "Connectivity" > "SPI1" 활성화.
- Parameter Settings:
- Mode: Full-Duplex Master.
- Data Size: 8-bit.
- Prescaler: 8 (SPI 클럭 = 170 MHz / 8 = 21.25 MHz).
- First Bit: MSB First.
- CPOL: Low, CPHA: 2 Edge (LIS3DH 요구사항).
- GPIO 설정:
- SPI1_SCK (PA5), SPI1_MOSI (PA7), SPI1_MISO (PA6) 설정.
- NSS (PA4)를 "GPIO_Output"으로 설정 (소프트웨어 제어).
- 디버깅 인터페이스 설정:
- "System Core" > "SYS"에서 Debug를 "Serial Wire" (SWD)로 설정 (PA13: SWDIO, PA14: SWCLK).
- 프로젝트 생성:
- Project Manager에서 Toolchain/IDE를 STM32CubeIDE로 설정.
- "Generate Code" 클릭.
3.2. I2C 및 SPI 동작 원리
- 시스템 초기화:
- 클럭 활성화 (__HAL_RCC_I2Cx_CLK_ENABLE(), __HAL_RCC_SPIx_CLK_ENABLE()).
- HAL_Init()으로 시스템 타이머 초기화.
- I2C 동작:
- I2C 핸들러 구조체 설정 (I2C_InitTypeDef).
- 마스터 모드에서 슬레이브 주소로 데이터 송수신.
- SPI 동작:
- SPI 핸들러 구조체 설정 (SPI_InitTypeDef).
- 마스터 모드에서 NSS 핀 제어 후 데이터 송수신.
- 인터럽트 (옵션):
- I2C 및 SPI 인터럽트 활성화 (HAL_I2C_EV_IRQHandler, HAL_SPI_IRQHandler).
- NVIC 설정으로 우선순위 지정.
4. I2C 및 SPI 예제 코드
4.1. 예제 1: I2C를 사용한 24C02 EEPROM 읽기/쓰기
I2C1 (PB6: SCL, PB7: SDA)을 사용하여 24C02 EEPROM에 데이터를 쓰고 읽습니다. 24C02는 2Kbit 메모리로, 8비트 주소와 7비트 슬레이브 주소(0x50)를 사용합니다.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* STM32CubeMX-generated code for I2C1 to read/write 24C02 EEPROM.
* System clock set to 170 MHz using HSE.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
I2C_HandleTypeDef hi2c1; // I2C1 핸들러 구조체
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define EEPROM_ADDR 0xA0 // 24C02의 7비트 슬레이브 주소 (0x50 << 1)
#define I2C_TIMEOUT 1000 // I2C 타임아웃 (ms)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile uint32_t i2cStatus = 0; // I2C 상태 변수: 0 (초기화 완료), 1 (동작 중), 0xFFFF (오류)
uint8_t writeData[2] = {0x10, 0xAA}; // EEPROM 주소 0x10에 0xAA 쓰기
uint8_t readData[1]; // EEPROM에서 읽은 데이터
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); // 시스템 클럭 설정 함수 선언
static void MX_I2C1_Init(void); // I2C1 초기화 함수 선언
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
HAL_Init(); // HAL 라이브러리 및 시스템 타이머(Systick) 초기화
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config(); // 시스템 클럭을 170 MHz로 설정
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_I2C1_Init(); // I2C1 초기화 (PB6: SCL, PB7: SDA)
/* USER CODE BEGIN 2 */
if (i2cStatus != 1) // I2C 초기화 성공 여부 확인
{
i2cStatus = 0xFFFF; // 초기화 실패 시 오류 상태 설정
while (1);
}
// 24C02 EEPROM에 데이터 쓰기 (주소 0x10에 0xAA 쓰기)
if (HAL_I2C_Mem_Write(&hi2c1, EEPROM_ADDR, writeData[0], I2C_MEMADD_SIZE_8BIT, &writeData[1], 1, I2C_TIMEOUT) != HAL_OK)
{
i2cStatus = 0xFFFF;
while (1);
}
HAL_Delay(5); // 24C02 쓰기 사이클 대기 (최대 5ms)
// 24C02 EEPROM에서 데이터 읽기 (주소 0x10에서 1바이트 읽기)
if (HAL_I2C_Mem_Read(&hi2c1, EEPROM_ADDR, writeData[0], I2C_MEMADD_SIZE_8BIT, readData, 1, I2C_TIMEOUT) != HAL_OK)
{
i2cStatus = 0xFFFF;
while (1);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (readData[0] == writeData[1]) // 읽은 데이터가 쓴 데이터(0xAA)와 일치하는지 확인
{
i2cStatus = 1; // 동작 성공
}
else
{
i2cStatus = 0xFFFF; // 오류 상태
}
HAL_Delay(100); // 100ms 대기
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 85;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x10909CEC; // 400 kHz 타이밍 (STM32CubeMX에서 생성)
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
i2cStatus = 1; // I2C 초기화 성공 표시
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
i2cStatus = 0xFFFF;
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */
설명:
- 기능: I2C1을 통해 24C02 EEPROM에 주소 0x10에 데이터 0xAA를 쓰고 읽기.
- 설정: PB6 (SCL), PB7 (SDA), 400 kHz, 7비트 주소 (0x50).
- 출력: 읽은 데이터가 0xAA와 일치하면 i2cStatus=1, 아니면 0xFFFF.
- 클럭: HSE 8 MHz, PLL로 170 MHz SYSCLK.
- 특이사항: 24C02는 쓰기 후 5ms 대기 필요. 외부 풀업 저항(4.7kΩ) 필수.
4.2. 예제 2: SPI를 사용한 LIS3DH 가속도 센서 데이터 읽기
SPI1 (PA5: SCK, PA6: MISO, PA7: MOSI, PA4: NSS)을 사용하여 LIS3DH 가속도 센서에서 X축 데이터를 읽습니다. LIS3DH는 3축 가속도 센서로, SPI 모드에서 8비트 데이터, CPOL=Low, CPHA=2 Edge를 사용합니다.
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* STM32CubeMX-generated code for SPI1 to read X-axis data from LIS3DH accelerometer.
* System clock set to 170 MHz using HSE.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
SPI_HandleTypeDef hspi1; // SPI1 핸들러 구조체
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define NSS_Pin GPIO_PIN_4 // PA4를 NSS로 사용
#define NSS_GPIO_Port GPIOA
#define SPI_TIMEOUT 1000 // SPI 타임아웃 (ms)
#define LIS3DH_WHO_AM_I 0x0F // WHO_AM_I 레지스터 주소
#define LIS3DH_CTRL_REG1 0x20 // 제어 레지스터 1
#define LIS3DH_OUT_X_L 0x28 // X축 저위 바이트
#define LIS3DH_OUT_X_H 0x29 // X축 고위 바이트
#define LIS3DH_READ 0x80 // 읽기 비트 (MSB=1)
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
volatile uint32_t spiStatus = 0; // SPI 상태 변수: 0 (초기화 완료), 1 (동작 중), 0xFFFF (오류)
uint8_t whoAmI; // WHO_AM_I 레지스터 값
int16_t xAccel; // X축 가속도 값
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void); // 시스템 클럭 설정 함수 선언
static void MX_SPI1_Init(void); // SPI1 초기화 함수 선언
static void MX_GPIO_Init(void); // GPIO 초기화 함수 선언
/* USER CODE BEGIN PFP */
void LIS3DH_Init(void); // LIS3DH 초기화 함수 선언
void LIS3DH_Read_X(void); // X축 데이터 읽기 함수 선언
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void LIS3DH_Init(void)
{
uint8_t txData[2];
uint8_t rxData[2];
// LIS3DH CTRL_REG1 설정: 100 Hz, 모든 축 활성화
txData[0] = LIS3DH_CTRL_REG1; // 레지스터 주소
txData[1] = 0x57; // 100 Hz (0101), X/Y/Z 축 활성화 (0111)
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, txData, 2, SPI_TIMEOUT);
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
// WHO_AM_I 레지스터 읽기 (0x33 예상)
txData[0] = LIS3DH_WHO_AM_I | LIS3DH_READ; // 읽기 모드
txData[1] = 0x00; // 더미 바이트
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, SPI_TIMEOUT);
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
whoAmI = rxData[1]; // WHO_AM_I 값 저장 (0x33)
}
void LIS3DH_Read_X(void)
{
uint8_t txData[2];
uint8_t rxData[2];
// X축 저위 바이트 읽기
txData[0] = LIS3DH_OUT_X_L | LIS3DH_READ;
txData[1] = 0x00;
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, SPI_TIMEOUT);
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
xAccel = rxData[1];
// X축 고위 바이트 읽기
txData[0] = LIS3DH_OUT_X_H | LIS3DH_READ;
txData[1] = 0x00;
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);
HAL_SPI_TransmitReceive(&hspi1, txData, rxData, 2, SPI_TIMEOUT);
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
xAccel |= (rxData[1] << 8); // 16비트 X축 가속도 값 조합
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
HAL_Init(); // HAL 라이브러리 및 시스템 타이머(Systick) 초기화
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config(); // 시스템 클럭을 170 MHz로 설정
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init(); // NSS 핀 초기화
MX_SPI1_Init(); // SPI1 초기화
/* USER CODE BEGIN 2 */
if (spiStatus != 1) // SPI 초기화 성공 여부 확인
{
spiStatus = 0xFFFF;
while (1);
}
LIS3DH_Init(); // LIS3DH 초기화
if (whoAmI != 0x33) // WHO_AM_I 값 확인
{
spiStatus = 0xFFFF;
while (1);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
LIS3DH_Read_X(); // X축 가속도 읽기
if (xAccel != 0) // 가속도 데이터 유효성 확인
{
spiStatus = 1; // 동작 성공
}
else
{
spiStatus = 0xFFFF; // 오류 상태
}
HAL_Delay(100); // 100ms 대기
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 85;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief SPI1 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; // LIS3DH 요구사항
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
spiStatus = 1; // SPI 초기화 성공 표시
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
/*Configure GPIO pin : PA4 */
GPIO_InitStruct.Pin = NSS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(NSS_GPIO_Port, &GPIO_InitStruct);
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
spiStatus = 0xFFFF;
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
while (1)
{
}
}
#endif /* USE_FULL_ASSERT */
설명:
- 기능: SPI1을 통해 LIS3DH 가속도 센서에서 X축 데이터를 읽음.
- 설정: PA5 (SCK), PA6 (MISO), PA7 (MOSI), PA4 (NSS, 소프트웨어 제어), 21.25 MHz, CPOL=Low, CPHA=2 Edge.
- 출력: WHO_AM_I (0x33) 확인 후 X축 가속도 값을 xAccel에 저장.
- 클럭: HSE 8 MHz, PLL로 170 MHz SYSCLK.
- 특이사항: LIS3DH는 100 Hz, 8비트 데이터, 읽기 시 MSB=1 설정 필요.
5. 추가 고려 사항
- 클럭 설정: HSE 주파수에 따라 PLL 설정 조정 필요.
- I2C 풀업 저항: SDA, SCL 라인에 외부 풀업 저항(4.7kΩ 권장) 필요.
- SPI NSS 관리: 소프트웨어 NSS 사용 시 GPIO 제어 주의.
- 디버깅: SWD 인터페이스로 실시간 데이터 모니터링.
- 저전력: I2C/SPI 비활성화 및 클럭 게이팅으로 전력 최적화.
키워드: STM32G474, I2C, SPI, HAL API, STM32CubeIDE, STM32CubeMX, 24C02 EEPROM, LIS3DH, 데이터 송수신, HSE 클럭, 디버깅
'MCU > STM32' 카테고리의 다른 글
[STM32G474] ADC 사용법: HAL API로 타이머 트리거, 하드웨어 트리거, 인터럽트, DMA 설정 및 코드 예제 (0) | 2025.08.19 |
---|---|
[STM32G474] ADC 사용법: HAL API로 단일 변환, 연속 변환, 스캔 모드 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] 타이머 트리거 사용법: HAL API로 타이머 트리거 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] 타이머 사용법: HAL API로 타이머 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] UART 사용법: HAL API로 UART 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32G474] GPIO 사용법: HAL API로 GPIO 설정 및 코드 예제 (0) | 2025.08.19 |
[STM32] 링커 스크립트: 상세 설명, 구조, 작성 방법 및 예제 (STM32 Linker Script: Detailed Explanation, Structure, Writing Methods, and Examples) (3) | 2025.08.18 |
[ STM32]STM32L432KC로 Modbus RTU Slave 코드 구현: DMA와 저전력 최적화 (0) | 2025.08.13 |