この記事は、Gemini、NotebookLMを補助的に使用して書いています。
1.カラーセンサを使った、色相識別
STM32F446REと浜松ホトニクス製カラーセンサー「S13683-02DT」を使用し、対象物の色(赤、緑、青、黄、紫など)を識別しました。
I2Cドライバの実装、読み書きドライバーを全面改造後、カラーセンサードライバーで、取得したRAWデータの活用、処理方法に、一工夫必要でした。
開発環境: nucleo-F446RE(STM32F446RE)

浜松ホトニクス製カラーセンサー「S13683-02DT」
RGBデータと、赤外線を含んだノイズ光の強さを測定します。通信は、I2C。

| ピン番号 | 機能 | ピン番号 | 機能 | ||
|---|---|---|---|---|---|
| 1 | VDD | 2.5Vor3.3V | 10 | SDA | I2C |
| 2 | (GND) | 9 | (GND) | ||
| 3 | (GND) | 8 | (GND) | ||
| 4 | (GND) | 7 | (GND) | ||
| 5 | GND | 6 | SCL | I2C |
(GND)は、ノイズ対策、フレームグランドと想定し、すべて5.GNDに繋げました。
初期化処理は、データシート通りで動きます。

コントロールレジスタの設定は、データシートでは、リスタートで2回目の送信を行っていますが、実装では、ストップコンディションを生成しながら、2回にわけて、設定値を送信しています。
[開発状況]
花子の矩形塗りつぶしを使って、色を変えます。
カラーセンサーは、PC画面の前に立たせて、読み取らせます。判定結果は、USBシリアルで、受信します。

2. 実装フェーズ:正規化と最短距離法
2.1 生データの観察
PC画面上のペイントソフトで色を表示し、センサー値を測定した結果、
単純な「R, G, Bの最大値」だけでは判定が難しい(例:黄色を表示しても、赤成分より緑成分の方が突出して高いなど、理論値と実測値にズレがある)。
明るさ(照明条件や距離)によって値の大きさが激変するため、閾値(しきい値)による判定は不安定、if文で処理することでは、解決できそうにないです。
室内、蛍光灯の光なので、測定値自体は、安定しています。PC画面のブルーLEDの影響が、出ます。
2.2 アルゴリズムの確立
これらの課題を解決するため、以下の2段階の処理を実装しました。
1.正規化(Normalization)
明るさの影響を排除し、「色の比率」のみを抽出する。$$r = \frac{R}{R+G+B}, \quad g = \frac{G}{R+G+B}, \quad b = \frac{B}{R+G+B}$$これにより、明るさが変わっても合計が1.0(100%)となる指標に変換します。明るさの影響が、これで、なくなります。暗い赤と、明るい赤の違いをキャンセルします。

実測に様々な色を測定し、そのデータを正規化して、stm32F446REに保存します。これを色見本として、色の特定に使用します。
// 実測データから算出した正規化値
const ColorFingerprint colorDB[] = {
{0.25f, 0.42f, 0.27f, "GRAY"}, //灰色
{0.70f, 0.21f, 0.09f, "RED"}, // 赤
{0.21f, 0.61f, 0.12f, "LIME"}, // ライム
{0.20f, 0.53f, 0.17f, "GREEN"}, // 緑
{0.15f, 0.67f, 0.13f, "YELLOWGREEN"}, //黄緑
{0.12f, 0.25f, 0.63f, "BLUE"}, // 青
{0.19f, 0.33f, 0.38f, "DARK BLUE"}, //紺色
{0.17f, 0.42f, 0.36f, "SKY BLUE"}, //空色
{0.35f, 0.54f, 0.11f, "YELLOW"}, // 黄 (R+GだがG強め)
{0.37f, 0.20f, 0.35f, "PURPLE"}, // 紫 (R+B)
{0.48f, 0.35f, 0.10f, "ORANGE"}, //オレンジ
{0.14f, 0.55f, 0.26f, "WATER"}, //水色
{0.32f, 0.30f, 0.33f, "PINK"}, //ピンク
{0.52f, 0.26f, 0.12f, "BROWN"} //茶色
};
2.最短距離法(Nearest Neighbor)
あらかじめ測定した複数色(赤・緑・青・黄・紫etc.)の正規化データを「辞書(基準点)」として登録。現在の測定値が、どの基準点に最も近いか(ユークリッド距離の二乗)を計算して判定します。
測定した色のベクトルを $(r, g, b)$、
比較対象(辞書)の色のベクトルを $(r_t, g_t, b_t)$ とすると、
その距離の二乗 $d^2$ は次のように表されます。
$$d^2 = (r - r_t)^2 + (g - g_t)^2 + (b - b_t)^2$$
全ての色見本の正規化されたデータと照合して、最小になったときの色見本を、測定対象の色相として、採用します。
// 判定関数
const char* determineColor(int16_t rawR, int16_t rawG, int16_t rawB) {
float total = rawR + rawG + rawB;
// 暗すぎる(黒)場合の除外処理
if (total < 100) return "BLACK";
// 2. 入力値を正規化(比率にする)
float r = rawR / total;
float g = rawG / total;
float b = rawB / total;
int bestIndex = -1;
float minDistance = 100.0f; // とりあえず大きな値
// 3. 辞書データと総当たりで比較(最短距離法)
for (int i = 0; i < S13683ColorNumbers; i++) {
// 各成分の差を計算
float diffR = r - colorDB[i].r;
float diffG = g - colorDB[i].g;
float diffB = b - colorDB[i].b;
// 距離の2乗を計算 (ユークリッド距離)
float dist = (diffR * diffR) + (diffG * diffG) + (diffB * diffB);
// 一番距離が近い(似ている)色を更新
if (dist < minDistance) {
minDistance = dist;
bestIndex = i;
}
}
return colorDB[bestIndex].name;
}
※STM32でsqrtを使うと、計算量が増える、また、最小値は、距離の相対的な差でわかるので、2乗のままにしています。
3.なぜこの手法でうまくいくのか?
3.2 概念の壁:正規化の幾何学的意味
Q:$$r = \frac{R}{R+G+B}, \quad g = \frac{G}{R+G+B}, \quad b = \frac{B}{R+G+B}$$という計算が、なぜ「正規化」と呼ばれるのか?どうしてこれが有効なのか?
解決のプロセス:
正規化されたベクトル $(r, g, b)$ は、3つの基底ベクトルを重み付けして足し合わせたもの(線形結合)として表現できます。
その「正規直交基底を最大(頂点)として、その間に収縮(内包)されている」というイメージは、数学的には 「凸結合(Convex Combination)」 および 「重心座標(Barycentric Coordinates)」 という概念で完璧に説明できます。
この視点を加えることで、色の理解が「物理」から「幾何学」へと完全に昇華されます。
解説:そのイメージの数学的証明
3次元空間の正規直行基底ベクトルをそれぞれ
$\vec{e}_R, \vec{e}_G, \vec{e}_B$ とします。
$\vec{e}_R = (1, 0, 0)$ : 純粋な赤
$\vec{e}_G = (0, 1, 0)$ : 純粋な緑
$\vec{e}_B = (0, 0, 1)$ : 純粋な青
これらは、正規化されたベクトル、3つの軸の「最大」となる点、つまり三角形の3つの頂点になります。
ベクトル和としての表現正規化された色ベクトル $\vec{v}$ は、この3つの頂点を $r, g, b$ の比率で引っ張り合った結果(ベクトル和)です。$$\vec{v} = r\vec{e}_R + g\vec{e}_G + b\vec{e}_B$$
$$r = \frac{R}{R+G+B}, \quad g = \frac{G}{R+G+B}, \quad b = \frac{B}{R+G+B}$$
「収縮されたベクトル」の意味ここで重要なのは、係数の条件 $r+g+b=1$ です。数学では、係数を足して1になるベクトル和のことを 「凸結合(とつけつごう)」 と呼びます。
凸結合で作られたベクトルは、必ず基底ベクトル(頂点)で囲まれた領域の内側に存在します。
つまり、元の大きなベクトルが、係数のバランスによって、3つの頂点が作る三角形の内側へと**「収縮(マッピング)」**された状態になります。
4. 最終的な概念到達点 ベクトル空間と正規化の数学的解釈
1.ベクトルのスカラー倍と方向保存:RGBデータをベクトル $\vec{v} = (R, G, B)$ と見なすとき、これに正のスカラー $k$ を掛けてもベクトルの向き(方向)は変化せず、大きさ(長さ)のみが変化する。$$k\vec{v} = (kR, kG, kB)$$これは、各直交基底(R軸, G軸, B軸)成分が**「すべて同じ比率 $k$ で伸縮している」**ことを意味する。物理的には、光の強さが変わっても、各色の成分比率は保たれる現象と対応する。
2.平面への射影による正規化:正規化計算におけるスカラー $k$ を $k = \frac{1}{R+G+B}$ と定義することは、ベクトルの先端を特定の平面上に移動させる操作に他ならない。この操作により、あらゆる明るさのベクトルは、各軸の切片が1である平面($x + y + z = 1$)との交点に変換(射影)される。
3.結論:「ベクトルの大きさを明るさ、向きを色相」 と捉え、正規化によって「すべてのベクトルを $x+y+z=1$ の平面上に整列させる」ことで、明るさの次元を排除し、色情報のみを純粋に取り出すことができる。
STM32F446RE カラーセンサーS13683-03DTドライバー
S13683.h
#ifndef INC_S13683_H_
#define INC_S13683_H_
#include "main.h"
#include "myI2C.h"
#include "myUtilities.h"
#include <math.h>
#define S13683DeviceAdd 0x54
#define S13683ColorNumbers 14
typedef union
{
uint16_t data[4];
uint8_t byteData[8];
}S13683_colorData_Struct;
typedef struct
{
S13683_colorData_Struct value;
bool GO;
}S13683_colorSenser_Struct;
// 1. 測定データから作った「色の辞書(指紋)」
// { R比率, G比率, B比率, 名前 }
typedef struct {
float r;
float g;
float b;
const char* name;
} ColorFingerprint;
extern const char* cName[];
extern const ColorFingerprint colorDB[];
extern S13683_colorSenser_Struct S13683;
extern int8_t S13683_Init();
extern int8_t S13683_Start();
extern void S13683_GetData(uint8_t *rxData);
extern const char* determineColor(int16_t rawR, int16_t rawG, int16_t rawB);
#endif /* INC_S13683_H_ */
S13683.c
/*
* S13683.c
*
* Created on: 2026/01/23
* Author: h
*/
#include "S13683.h"
/*
* S13683制御構造体
*/
S13683_colorSenser_Struct S13683;
// 実測データから算出した正規化値
const ColorFingerprint colorDB[] = {
{0.25f, 0.42f, 0.27f, "GRAY"}, //灰色
{0.70f, 0.21f, 0.09f, "RED"}, // 赤
{0.21f, 0.61f, 0.12f, "LIME"}, // ライム
{0.20f, 0.53f, 0.17f, "GREEN"}, // 緑
{0.15f, 0.67f, 0.13f, "YELLOWGREEN"}, //黄緑
{0.12f, 0.25f, 0.63f, "BLUE"}, // 青
{0.19f, 0.33f, 0.38f, "DARK BLUE"}, //紺色
{0.17f, 0.42f, 0.36f, "SKY BLUE"}, //空色
{0.35f, 0.54f, 0.11f, "YELLOW"}, // 黄 (R+GだがG強め)
{0.37f, 0.20f, 0.35f, "PURPLE"}, // 紫 (R+B)
{0.48f, 0.35f, 0.10f, "ORANGE"}, //オレンジ
{0.14f, 0.55f, 0.26f, "WATER"}, //水色
{0.32f, 0.30f, 0.33f, "PINK"}, //ピンク
{0.52f, 0.26f, 0.12f, "BROWN"} //茶色
};
/**
* @fn int8_t S13683_Init()
* @brief S16834センサー初期化
*
* @return
*/
int8_t S13683_Init()
{
uint8_t i,val;
int8_t ret=-1;
//コントロールレジスタ初期化
//コントロールレジスタ ADCリセット
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x00,0x84);
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x00,0x04);
//積分時間 定数倍 (初期値0xC30)/2=0x618
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x01,0x06);
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x02,0x18);
for(i=0x00; i<=0x02; i++)
{
val = I2Cx_b1Read(I2C1, S13683DeviceAdd, i);
printf("[%X(hex)] %d(dec)\r", val, val);
}
return ret;
}
//シリアル表示用文字列
const char* cName[] = {"R :","G :","B :", "IR:"};
/**
* @fn int8_t S13683_Start()
* @brief
* S13683計測開始
* @return i2c通信成否
*/
int8_t S13683_Start()
{
uint8_t ret=-1;
//コントロールレジスタ ADCリセット
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x00,0x84);
ret = I2Cx_b2Write(I2C1,S13683DeviceAdd,0x00,0x04);
S13683.GO = true;
return ret;
}
/**
* @fn void S13683_GetData(uint8_t*)
* @brief
* S13683 データ受信
* @param rxData i2c受信バッファ
*/
void S13683_GetData(uint8_t *rxData)
{
uint8_t i;
float sum;
float fdata[3];
//データ読み出し
I2Cx_bnRead(I2C1,S13683DeviceAdd,0x03,I2Cx_rxData,8);
sum=0;
//8bit受信データを復元16ビット化
for(i=0; i<4; i++)
{
S13683.value.byteData[2*i+1]=I2Cx_rxData[2*i];
S13683.value.byteData[2*i] = I2Cx_rxData[2*i+1];
//printf("[%x ",I2Cx_rxData[2*i]);
//printf("%x ]",I2Cx_rxData[2*i+1]);
printf("%s %X(hex) %d(dec)\r",cName[i], S13683.value.data[i],S13683.value.data[i]);
sum += (float)S13683.value.data[i];
}
//正規化表示(コピペで教師データ)
for(i=0; i<3; i++)
{
fdata[i] = ((float)S13683.value.data[i])/sum;
printf("%1.2f ", fdata[i]);
}
//積分時間より長く待機 546x4=2184ms/2
//mainのタイマーで確保する。
}
/**
* @fn const char determineColor*(int16_t, int16_t, int16_t)
* @brief 最短距離法で色相識別
*
* @param rawR rawデータR
* @param rawG rawデータG
* @param rawB rawデータB
* @return
*/
const char* determineColor(int16_t rawR, int16_t rawG, int16_t rawB) {
float total = rawR + rawG + rawB;
// 暗すぎる(黒)場合の除外処理
if (total < 100) return "BLACK";
// 2. 入力値を正規化(比率にする)
float r = rawR / total;
float g = rawG / total;
float b = rawB / total;
int bestIndex = -1;
float minDistance = 100.0f; // とりあえず大きな値
// 3. 辞書データと総当たりで比較(最短距離法)
for (int i = 0; i < S13683ColorNumbers; i++) {
// 各成分の差を計算
float diffR = r - colorDB[i].r;
float diffG = g - colorDB[i].g;
float diffB = b - colorDB[i].b;
// 距離の2乗を計算 (ユークリッド距離)
float dist = (diffR * diffR) + (diffG * diffG) + (diffB * diffB);
// 一番距離が近い(似ている)色を更新
if (dist < minDistance) {
minDistance = dist;
bestIndex = i;
}
}
return colorDB[bestIndex].name;
}
main.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "myUsart.h"
#include "myTimer.h"
#include "myI2C.h"
#include "mySPI.h"
#include "S13683.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM2_Init(void);
static void MX_I2C1_Init(void);
static void MX_SPI2_Init(void);
/* 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 */
uint8_t scanCount;
uint32_t portValue;
char *colorName;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
/* System interrupt init*/
NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_TIM2_Init();
MX_I2C1_Init();
MX_SPI2_Init();
/* USER CODE BEGIN 2 */
USART_Initialize_Printf();
printf("testString\r");
MCP23017_Init(MCP23017DeviceAdd);
portValue=0x55;
S13683_Init();
scanCount=0;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(USART2RX.rxCompleted==true)
{
USART2RX.rxCompleted=false;
USART2RX.length=0;
printf("%s\r",USART2RX.buf);
USART2->CR1 |= USART_CR1_RXNEIE;
}
if(tm2.completed==true)
{
tm2.completed=false;
LL_GPIO_TogglePin(GPIOA,LL_GPIO_PIN_5);
I2Cx_b2Write(I2C1, MCP23017DeviceAdd, 0x0A, portValue);
if(portValue==0x55)
{
portValue=0xAA;
}else{
portValue=0x55;
}
//ColorSenser Scan
if((scanCount==0) && (S13683.GO==false))
{
S13683_Start();
S13683.GO=true;
printf("ScanStart\r");
}
scanCount++;
if(scanCount>=3 && S13683.GO==true)
{
scanCount=0;
S13683.GO = false;
S13683_GetData(I2Cx_rxData);
colorName = determineColor(S13683.value.data[0],S13683.value.data[1],S13683.value.data[2]);
printf("%s \r\r",colorName);
}
//Timer2 reStart
LL_TIM_EnableIT_UPDATE(TIM2);
}
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
LL_FLASH_SetLatency(LL_FLASH_LATENCY_5);
while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_5)
{
}
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
LL_PWR_EnableOverDriveMode();
LL_RCC_HSE_Enable();
/* Wait till HSE is ready */
while(LL_RCC_HSE_IsReady() != 1)
{
}
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_4, 180, LL_RCC_PLLP_DIV_2);
LL_RCC_PLL_Enable();
/* Wait till PLL is ready */
while(LL_RCC_PLL_IsReady() != 1)
{
}
while (LL_PWR_IsActiveFlag_VOS() == 0)
{
}
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_4);
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_2);
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
/* Wait till System clock is ready */
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
{
}
LL_Init1msTick(180000000);
LL_SetSystemCoreClock(180000000);
LL_RCC_SetTIMPrescaler(LL_RCC_TIM_PRESCALER_TWICE);
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
LL_I2C_InitTypeDef I2C_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
/** I2C Initialization
*/
LL_I2C_DisableOwnAddress2(I2C1);
LL_I2C_DisableGeneralCall(I2C1);
LL_I2C_EnableClockStretching(I2C1);
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
I2C_InitStruct.ClockSpeed = 100000;
I2C_InitStruct.DutyCycle = LL_I2C_DUTYCYCLE_2;
I2C_InitStruct.OwnAddress1 = 0;
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
LL_I2C_Init(I2C1, &I2C_InitStruct);
LL_I2C_SetOwnAddress2(I2C1, 0);
/* USER CODE BEGIN I2C1_Init 2 */
//LL_I2C_Enable(I2C1);
/* USER CODE END I2C1_Init 2 */
}
/**
* @brief SPI2 Initialization Function
* @param None
* @retval None
*/
static void MX_SPI2_Init(void)
{
/* USER CODE BEGIN SPI2_Init 0 */
/* USER CODE END SPI2_Init 0 */
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
/**SPI2 GPIO Configuration
PC1 ------> SPI2_MOSI
PC2 ------> SPI2_MISO
PB10 ------> SPI2_SCK
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN SPI2_Init 1 */
/* USER CODE END SPI2_Init 1 */
/* SPI2 parameter configuration*/
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_1EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 10;
LL_SPI_Init(SPI2, &SPI_InitStruct);
LL_SPI_SetStandard(SPI2, LL_SPI_PROTOCOL_MOTOROLA);
/* USER CODE BEGIN SPI2_Init 2 */
LL_SPI_Enable(SPI2);
/* USER CODE END SPI2_Init 2 */
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
LL_TIM_InitTypeDef TIM_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
/* TIM2 interrupt Init */
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
TIM_InitStruct.Prescaler = 16;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0x15F9;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_Init(TIM2, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM2);
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM2);
/* USER CODE BEGIN TIM2_Init 2 */
LL_TIM_EnableCounter(TIM2);
LL_TIM_EnableIT_UPDATE(TIM2);
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
LL_USART_InitTypeDef USART_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
/**USART2 GPIO Configuration
PA2 ------> USART2_TX
PA3 ------> USART2_RX
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_2|LL_GPIO_PIN_3;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART2 interrupt Init */
NVIC_SetPriority(USART2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
USART_InitStruct.BaudRate = 115200;
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART2, &USART_InitStruct);
LL_USART_ConfigAsyncMode(USART2);
LL_USART_Enable(USART2);
/* USER CODE BEGIN USART2_Init 2 */
LL_USART_EnableIT_RXNE(USART2);
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOC);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
/**/
LL_GPIO_ResetOutputPin(GPIOA, LD2_Pin|LL_GPIO_PIN_10);
/**/
LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_5);
/**/
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTC, LL_SYSCFG_EXTI_LINE13);
/**/
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_13;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);
/**/
LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_13, LL_GPIO_PULL_NO);
/**/
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_INPUT);
/**/
GPIO_InitStruct.Pin = LD2_Pin|LL_GPIO_PIN_10;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/**/
GPIO_InitStruct.Pin = LL_GPIO_PIN_5;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* EXTI interrupt init*/
NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(EXTI15_10_IRQn);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#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)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
