0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

カラーセンサー(S13683-03DT)による色判定 nucleo-F446RE

0
Last updated at Posted at 2026-01-24

この記事は、Gemini、NotebookLMを補助的に使用して書いています。

1.カラーセンサを使った、色相識別

STM32F446REと浜松ホトニクス製カラーセンサー「S13683-02DT」を使用し、対象物の色(赤、緑、青、黄、紫など)を識別しました。
I2Cドライバの実装、読み書きドライバーを全面改造後、カラーセンサードライバーで、取得したRAWデータの活用、処理方法に、一工夫必要でした。

開発環境: nucleo-F446RE(STM32F446RE)
IMG_5503.JPG

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

ピン番号 機能 ピン番号 機能
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に繋げました。

初期化処理は、データシート通りで動きます。
データシート初期化処理.png
コントロールレジスタの設定は、データシートでは、リスタートで2回目の送信を行っていますが、実装では、ストップコンディションを生成しながら、2回にわけて、設定値を送信しています。

[開発状況]
花子の矩形塗りつぶしを使って、色を変えます。
カラーセンサーは、PC画面の前に立たせて、読み取らせます。判定結果は、USBシリアルで、受信します。
IMG_5505A.jpg

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%)となる指標に変換します。明るさの影響が、これで、なくなります。暗い赤と、明るい赤の違いをキャンセルします。
Figure_1.png
実測に様々な色を測定し、そのデータを正規化して、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$$

全ての色見本の正規化されたデータと照合して、最小になったときの色見本を、測定対象の色相として、採用します。

最短距離法.png

ユークリッド距離の二乗
// 判定関数
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
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
/*
 * 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
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 */

STM32のFPU(Floating Point Unit)を有効にする。

コード内の定数は、0.4fのように、fをおしりにつけること。

double(64bit)を混ぜない: F446REのFPUは「単精度専用」です。一度でも double 型の変数が混ざると、そこだけソフトウェア計算(数倍〜数十倍遅い)に切り替わります。

標準ライブラリの関数名: sqrt() や abs() ではなく、sqrtf() や fabsf() のように、末尾に f がついた単精度用の関数を明示的に使うのがベストプラクティスです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?