実験10 K210とSTM32の通信による、ビジョンを用いた物体の中心位置と色の認識
実験の目的:
-
AIモジュール(K210)で物体を認識し、その結果をSTM32に送信してシリアルポートに出力します。
-
STM32 GPIO周辺機能ピンの設定方法を習得し、USART・DMAの設定ができるようにします。
-
STM32CubeMX で新規プロジェクトを作成し、MDK_ARMプロジェクトファイルを生成できるようにします。
-
keil IDE でユーザーコードを追加し、プロジェクトのテストを行えるようにします。
機能要件:
物体の中心位置と色の情報をリアルタイムに取得し、シリアルポートに出力します(将来的にはビジュアルトラッキングのプロジェクトに応用できます)。
ハードウェアの準備:
STM32メインボード V1.3 * 1、ST-Linkダウンロードケーブル * 1、3Sバッテリーボックス * 1、リチウム電池 * 3、AIモジュール(K210)* 1、Type-Cデータケーブル * 1
回路接続:
ハードウェアモジュール
ヒント:まず開発ボード、環境設定、電源モジュールの基本的な使い方に慣れておくことを強くおすすめします。電子モジュールについては必要に応じて参照してください。
UNI-STM32開発ボード【V1.3】の紹介
UNI-STM32開発環境の設定【V2.0】
バッテリーおよび電源モジュール使用上の注意(必読)
電子モジュールの紹介(AIモジュールの基本的な使い方はこちらをご覧ください)
ピンの説明
| AIモジュール(K210) | STM32開発ボード |
|---|---|
| 5V | 5V |
| GND | GND |
| RX | USART3_TX |
| TX | USART3_RX |
まず STM32開発ボード上のK210インターフェース の位置を確認し、ピン表に従って配線してください。


実物の配線
ヒント:AIモジュール(K210)とSTM32開発ボードの接続には、AIモジュール(K210)付属の黒い接続ケーブルを使用することを強くおすすめします。
ジャンパーワイヤ(デュポンケーブル)で接続すると、シリアルポートでデータを受信できない場合があります。

ソフトウェア設計:
設計フロー
AIモジュール(K210)が画像データを取得し、認識結果をSTM32に送信します。

STM32がデータを受信し、シリアルデバッグツールでデータを確認できます。

プロジェクトのダウンロード
ソースコードファイル:Receive_Vision_Msg.zip
プログラムコード
完全なプロジェクトコードは(ソースコードファイル)をクリックしてダウンロードしてください。
AIモジュール(K210)のプログラム
1. 物体のLab色空間の値を取得する
'''
功能:实现检测特定物体,并将该物体的lab色域值范围打印出来
日期:2026-04-09
'''
import sensor
import image
import time
import lcd
#载入按键控制模块
from hiwonder import hw_key, hw_led , fill_light
#初始化LCD
lcd.init()
#以下是初始化传感器
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 100)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
#创建LED控制对象
led = hw_led()
#创建补光灯控制对象
f_led = fill_light()
#LED,补光灯开
#led.led_onoff(1) #LED开
#led.led_onoff(0) #LED关
#f_led.fill_onoff(0) #补光灯开
#time.sleep(0.5) #延时0.5s
#帧率时钟
clock = time.clock()
#方框参数
r = [(320//2)-(50//2), (240//2)-(50//2), 50, 50]
#创建按键控制对象
key = hw_key()
state = key.key_scan(1)
#显示图像,等待按键按下
while (state == 0):
state = key.key_scan(1)
img = sensor.snapshot()
img.draw_rectangle(r)
lcd.display(img)
print("Start learning ...")
threshold = [50, 50, 0, 0, 0, 0] # Middle L, A, B values.
#第一次赋值
img = sensor.snapshot()
hist = img.get_histogram(roi=r)
lo = hist.get_percentile(0.01) #获得直方图在1%范围内的CDF(根据需要调整)!
hi = hist.get_percentile(0.99) #获得直方图在99%范围内的CDF(根据需要调整)!
threshold[0] = (threshold[0] + lo.l_value()) // 2
threshold[1] = (threshold[1] + hi.l_value()) // 2
threshold[2] = (threshold[2] + lo.a_value())
threshold[3] = (threshold[3] + hi.a_value())
threshold[4] = (threshold[4] + lo.b_value())
threshold[5] = (threshold[5] + hi.b_value())
#连续捕捉了50张图像学习阶段
for i in range(50):
img = sensor.snapshot()
hist = img.get_histogram(roi=r)
lo = hist.get_percentile(0.01) #获得直方图在1%范围内的CDF(根据需要调整)!
hi = hist.get_percentile(0.99) #获得直方图在99%范围内的CDF(根据需要调整)!
#hi = hist.get_percentile(0.70) #获得直方图在99%范围内的CDF(根据需要调整)!
#以百分位数表示的平均值
threshold[0] = (threshold[0] + lo.l_value()) // 2
threshold[1] = (threshold[1] + hi.l_value()) // 2
threshold[2] = (threshold[2] + lo.a_value()) // 2
threshold[3] = (threshold[3] + hi.a_value()) // 2
threshold[4] = (threshold[4] + lo.b_value()) // 2
threshold[5] = (threshold[5] + hi.b_value()) // 2
#将识别区域用方框圈出来
for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
img.draw_rectangle(r, color=(0,255,0))
lcd.display(img)
print("threshold:", threshold)
#loop
while(True):
#用于计算帧率的函数,这里表示开始计时
clock.tick()
#从传感器捕获一张图像
img = sensor.snapshot()
#遍历图像中找到的颜色区块
for blob in img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
#绘制矩形和十字标记
img.draw_rectangle(blob.rect())
img.draw_cross(blob.cx(), blob.cy())
#显示在LCD上
lcd.display(img)
#打印帧率
#print(clock.fps())
2. Lab色空間の値をシリアル通信プログラムに書き込む
'''
功能: 将检测得到的红色/蓝色物体数据通过封装的串口通讯协议发送
日期:2026-04-09
'''
import binascii
import sensor
import image
import time
import lcd
import _thread
from hiwonder import hw_key, hw_led , fill_light, hw_uart
# 创建串口对象
serial = hw_uart()
"""
视觉颜色检测封装类
"""
class VisionModule:
def __init__(self):
# 初始化LCD
lcd.init()
# 初始化传感器
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(time = 100)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
# 帧率时钟
self.clock = time.clock()
# 创建LED控制对象
led = hw_led()
# 创建补光灯控制对象
f_led = fill_light()
# 打开补光灯(根据需要修改)
#led.led_onoff(1) # LED开
#led.led_onoff(0) # LED关(若需关闭则取消注释)
#f_led.fill_onoff(1) # 补光灯开
#time.sleep(0.5) # 延时0.5s
#LED,补光灯开
led.led_onoff(1) #LED开
led.led_onoff(0) #LED关
f_led.fill_onoff(0) #补光灯开
time.sleep(0.5) #延时0.5s
# 储存颜色的LAB阈值(示例仅红、蓝,可自行添加更多)
self.color_thresholds = [
#(42, 80, 21, 54, 22, 42), #Red1
#(37, 76, 21, 45, 37, 51), #Red2
#(33, 81, 28, 55, -4, 19), #Red3
#(44, 76, 24, 52, 41, 53), #Red4
#(29, 57, 37, 52, 21, 37), #Red5
(29, 81, 21, 55, -4, 53), #Red 红色色域值数据融合结果
#(28, 71, -33, -14, -11, 4), #blue1
#(24, 71, -4, 12, -41, -21), #blue2
#(46, 78, -6, 15, -53, -33), #blue3
#(31, 71, -20, -4, -27, -6), #blue4
#(21, 53, -18, -2, -25, -4), #blue5
#(45, 74, -10, 7, -45, -23), #blue6
#(29, 76, -41, -22, -7, 14), #blue7
#(38, 79, -27, -12, -21, -3), #blue8
#(21, 79, -41, 15, -53, 14), #Blue 蓝色色域值数据融合结果
(12, 31, 6, 23, -46, -21), #Blue 蓝色色域值数据融合结果
# (23, 52, -29, -9, -9, 7), # Green
# (65, 78, -10, -5, 38, 50), # Yellow
# (20, 50, 17, 37, -34, -14), # purple
]
self.color_strings = ['Red', 'Blue', 'Green', 'Yellow', 'purple', 'Other1', "Other2", "Other3", "Other4", "Other5"]
class FuncTag:
"""功能号定义"""
START = 0
FIND_MULTI_COLOR = 0x10
def str_2_int(data_str):
"""字符串转int"""
bb = binascii.hexlify(data_str)
bb = str(bb)[2:-1]
int_h = int(bb[0]) * 16
int_l = int(bb[1], 16)
return int_h + int_l
def int_to_hex_str(byte_val):
"""将整数转换为两位十六进制字符串"""
hex_str = hex(byte_val)
# 去掉 '0x' 前缀,并转换为大写
hex_str = hex_str[2:].upper()
# 确保是两位
if len(hex_str) == 1:
hex_str = '0' + hex_str
return hex_str
def send_data(img, x, y, w, h, msg):
'''
发送数据(协议头 0xAA 0x55 + 功能号 + 数据长度 + 数据 + 回车符)
将原来的CRC校验位替换为\n(回车)
Args:
x: 检测框中心x坐标
y: 检测框中心y坐标
w: 检测框宽度
h: 检测框高度
msg: 消息内容
'''
START_1 = 0xAA
START_2 = 0x55
FUNC_num = FuncTag.FIND_MULTI_COLOR # 功能编号
Length = 0 # 数据长度
data = [] # 数据组
# 参数都为0
if x == 0 and y == 0 and w == 0 and h == 0:
pass
else:
# x(小端模式)
low = x & 0xFF # 低位
high = x >> 8 & 0xFF # 高位
data.append(low)
data.append(high)
# y(小端模式)
low = y & 0xFF # 低位
high = y >> 8 & 0xFF # 高位
data.append(low)
data.append(high)
# w(小端模式)
low = w & 0xFF # 低位
high = w >> 8 & 0xFF # 高位
data.append(low)
data.append(high)
# h(小端模式)
low = h & 0xFF # 低位
high = h >> 8 & 0xFF # 高位
data.append(low)
data.append(high)
# msg
color_r = 0
color_g = 0
color_b = 0
position_x = 0
position_y = 0
if msg is not None:
if msg == "Red":
color_r = 255
color_g = 0
color_b = 0
position_x = 46
position_y = 0
img.draw_string(0, 0, "SenRed:", color=(color_r, color_g, color_b), scale=1)
elif msg == "Blue":
color_r = 0
color_g = 0
color_b = 255
position_x = 52
position_y = 20
img.draw_string(0, 20, "SendBlue:", color=(color_r, color_g, color_b), scale=1)
else:
color_r = 255
color_g = 255
color_b = 255
position_x = 56
position_y = 30
img.draw_string(0, 30, "SendOther:", color=(color_r, color_g, color_b), scale=1)
for i in range(len(msg)):
msg_int = str_2_int(msg[i])
data.append(msg_int)
Length += len(data)
send_buf = [FUNC_num, Length]
for i in range(len(data)):
send_buf.append(data[i])
send_buf.insert(0, START_1) # 插入协议头1
send_buf.insert(1, START_2) # 插入协议头2
send_buf.append(0x0A) # 加入回车符 \n (0x0A),替换原来的CRC校验位
serial.send_bytearray(send_buf) # 发送数据
# 将字节数组转换为十六进制字符串显示
hex_str_list = [int_to_hex_str(b) for b in send_buf]
hex_str = ' '.join(hex_str_list)
img.draw_string(position_x, position_y, hex_str, color=(color_r, color_g, color_b), scale=1)
def main():
# 创建视觉检测管理实例
vision = VisionModule()
while True:
#用于计算帧率的函数,这里表示开始计时
#从传感器捕获一张图像
img = sensor.snapshot()
#遍历多种颜色
for color_idx, threshold in enumerate(vision.color_thresholds):
#遍历图像中找到的颜色区块
blobs = img.find_blobs([threshold], pixels_threshold=100, area_threshold=100, merge=True, margin=10)
#是否找到
if blobs:
area_max = 0
blob_max = blobs[0]
#寻找最大的颜色块
for blob in blobs:
area = blob.w()*blob.h()
if(area_max < area):
blob_max = blob
area_max = area
if area_max < 2000:
continue
#画方框
color_set = (255, 255, 255)
if color_idx == 0:
color_set = (255, 0, 0) # 红色框
elif color_idx == 1:
color_set = (0, 0, 255) # 蓝色框
elif color_idx == 2:
color_set = (0, 255, 0) # 绿色框
else:
color_new = (255, 255, 255)
img.draw_rectangle(blob_max.rect(), color=color_set, thickness=5)
img.draw_cross(blob_max.cx(), blob_max.cy(), color=(255, 255, 0), thickness=5)
img.draw_string(blob_max.cx() + 10, blob_max.cy() - 10, vision.color_strings[color_idx], color=(color_set))
#将方框数据与消息赋值
send_x = blob_max.cx()
send_y = blob_max.cy()
send_w = blob_max.w()
send_h = blob_max.h()
#发送数据
send_data(img, send_x,send_y,send_w,send_h, vision.color_strings[color_idx])
#显示在LCD上
lcd.display(img)
#打印帧率
#print(clock.fps())
if __name__ == '__main__':
main()
下位機STM32のメインプログラム
以下はSTM32のメインプログラム 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"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "config.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);
/* 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--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* 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_DMA_Init();
MX_USART1_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
HAL_Delay(20);
printf("STM32 Program Started!\r\n");
printf("UART3 RX Interrupt Enabled\r\n");
HAL_UART_Receive_IT(&huart3, &rx_data, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//如果STM32接收到视觉模块发送到的信息(此时recv_flag值为true)
if(recv_flag == true){
printf("------------------------\r\n");
printf(" Func: 0x%02X\r\n", received_data.func_num); //功能码
printf(" Position: X=%d\r\n", received_data.x); //中心x坐标
printf(" Position: Y=%d\r\n", received_data.y); //中心y坐标
printf(" Size: W=%d\r\n", received_data.w); //矩形框的宽度
printf(" Size: H=%d\r\n", received_data.h); //矩形框的高度
printf(" Color: %s\r\n", received_data.color_str); //打印识别到的颜色(字符串)
clear(); //清空本次识别到的信息
}
HAL_Delay(50);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
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 = 4;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
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_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* 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 */
操作手順:
全体の流れ:
(1)STM32CubeMXでピン割り当てと周辺機能の初期化設定を行い、設定コードを生成します。
(2)keil5でMDK_ARMプロジェクトファイルを開き、コードの編集・コンパイル・デバッグを行います。
基本パラメータ:
本ドキュメントの(ハードウェアモジュール)に従って開発環境を設定した後の基本パラメータは次のとおりです。
ソフトウェアバージョン:STM32CubeMX V6.14.0
ハードウェアプラットフォーム:STM32F407IGT6
コンパイル環境:MDK_ARM V5.3.2
デバッグツール:ST-LINK/V2
1. 新規プロジェクトの作成とMCUの設定
1.1 MCUの選択
- STM32CubeMX を起動し、File > New Project をクリックします。

2. MCU の絞り込み条件を設定して STM32F407IGT6 を選択し、型番の列をクリックしてから、Start Project をクリックしてプロジェクトの設定を開始します。
- プロジェクト設定画面は次のとおりです。

1.2 クロック設定とコア設定
- Pinout & Configuration の System Core で、RCC(リセットおよびクロック制御) をクリックします。

- HSE クロックソースを Crystal/Ceramic Resonator(水晶振動子またはセラミック発振子)に設定します。

- 設定に成功すると、対応するピン(OSC_IN と OSC_OUT)が自動的に設定されます(下図の緑色の部分を参照)。

機能:
- 外部高速水晶発振器(HSE)を接続し、システムに高精度なクロック源を供給します。
**RCC_OSC_IN**:水晶入力ピン。**RCC_OSC_OUT**:水晶出力ピン(フィードバック信号)。- デフォルト機能:
**PH0**:**OSC_IN**(外部水晶入力)。**PH1**:**OSC_OUT**(外部水晶出力)。
- デバッグインターフェースのピンを設定します。System Core で SYS をクリックし、Debug モードを Serial Wire に設定します。

設定が完了すると、対応するピン出力(pinout)が緑色になります。

緑色の部分をクリックすると、詳細を確認できます。

1.3 クロックツリーの設定
次の順序でクロックパラメータを設定します。1. input frequency の値を 8 に変更、2. HSE を選択、3. PLLCLK を選択、4. HCLK の値を 84 に変更。その他のパラメータはデフォルトのままで構いません。

ヒント:設定時にはエラーが自動的に検出されます。周波数の設定が誤っている場合は、赤くハイライトされてエラーが表示されます。
2. MCU初期化パラメータの設定
2.1 USARTシリアルポートの設定
- PA9 と PA10 を USART1_TX と USART1_RX に設定します。GPIOをクリックし、ピンを設定します(この時点ではUSART1が未設定のため、ピンに黄色の警告が表示されます)。

- Connectivity → USART1 をクリックし、モードを Asynchronous に設定します(USART1の設定後、関連ピンが緑色で表示され、ピン設定が正常であることを示します)。

2.2 プロジェクト管理の設定
- デフォルトの画面は次のとおりです。

プロジェクト名を入力します(任意に設定してください)。
プロジェクトの保存先を選択します(Cドライブには保存しないでください)。
コンパイル環境として MDK_ARM を選択します。
バージョン V5.3.2 を選択します。
ファームウェアパッケージ名とバージョン。
(ヒント:デフォルトでは Use latest available version にチェックが入っています。ただし、PCに最新バージョンがない場合はエラーになるため、パッケージを各自でダウンロードしてローカルにインストールする必要があります。インストール方法は下記リンクを参照してください。
UNI-STM32開発環境の設定【V2.0】(リンク先の「7. よくある設定Q&A」のQ3に解決方法があります)
上記の設定が完了すると、下図のようになります。

2.Project Mannager > Code Generator ウィンドウを次のように設定します。

3.Project Mannager > Advanced Settings ウィンドウで、HAL ライブラリを選択します。

- プロジェクトを保存します:File → Save Project をクリックします。

3. GPIO設定:AIモジュール(K210)
ヒント:USART1 はSTM32メインボードとPCとの通信用インターフェース、USART3 はAIモジュール(K210)とSTM32との通信用インターフェースです。
| ピン | 機能 |
|---|---|
| PB10 | USART3_TX |
| PB11 | USART3_RX |
- ピンの設定。
(1)GPIOをクリックし、Pinout view 画面下部で PB10 を検索すると、該当するピンが点滅します。点滅しているピンを左クリックし、USART3_TX を選択します。続いて画面下部で PB11 を検索すると、該当するピンが点滅します。点滅しているピンを左クリックし、USART3_RX を選択します(この時点ではUSART3が未設定のため、ピンに黄色の警告が表示されます)。

(2)Connectivity → USART3 をクリックし、モードを Asynchronous に設定します(USART3の設定後、関連ピンが緑色で表示され、ピン設定が正常であることを示します)。

(3)割り込みの設定。NVIC Settings をクリックし、USART3の割り込みを Enabled に設定します(チェックボックスにチェックを入れます)。
(4)DMAの設定。DMA Settings をクリックし、Add ボタンをクリックします。

Select 列が表示されるので、Select をクリックして USART3_TX を選択します。

選択後、表示されるタブの設定はデフォルトのままで構いません。設定後のパラメータは下図のとおりです。

続けて Add をクリックし、表示された Select 列で Select をクリックして USART3_RX を選択します。

設定後の状態は下図のとおりです。これでDMAの設定は完了です。

- プロジェクトファイルを再度保存します。
ヒント:プロジェクトファイルは必ず再度保存してください(主にパラメータを保存するためです)。File → Save Project をクリックします。
4. コードの生成
1. GENERATE CODE をクリックしてコードを生成します。
- コードが自動生成されます。
- コードの生成に成功しました。
5. ユーザーコードの追加
上記で生成したフレームワークをもとに、keilエディタでユーザーコードを追加し、コンパイル・デバッグを行います。
ヒント1:例えば main.c の /* USER CODE BEGIN xx */ と /* USER CODE END xx */ の間にユーザーコードを追加します。
ヒント2:.c ファイルを独自に作成することもできます。
ヒント3:ユーザーコードは自分で記述しても構いません。(コード解説)に掲載したサンプルを参考に学習することもできます。
6. コードのダウンロードとテスト
-
プロジェクトをダウンロードします。ここではデモを用意しています(本ドキュメントの「ソフトウェア設計 → プロジェクトのダウンロード」から直接ダウンロードできます)。
-
本ドキュメントの 回路接続 に従って実物の配線を行います。
-
STM32のプログラムを書き込みます。Keilで拡張子が .uvprojx のプロジェクトファイルを開き、Load を実行します。

書き込み方法の詳細は、リンク先のUNI-STM32開発環境の設定【V2.0】の「5. プログラムのダウンロード」を参照してください。
注意:STM32開発ボードへのプログラム書き込みが完了したら、ST-LinkをPCから外してください。
- AIモジュール(K210)にプログラムを書き込み、スタンドアロンで動作させます。
(1) Type-Cデータケーブルの一端をAIモジュールに、もう一端をPCに接続します。

(2) CanMV IDEで serial_send_msg.py を開きます。
(ヒント:pyファイルの相対パス:Receive_Vision_Msg\VisionProgram\2.将Lab色域值写入串口程序中)

(3) 接続ボタンをクリックすると接続画面が表示されます。「詳細設定」にチェックを入れ、Mode-3 を選択して OK をクリックし、接続を開始します。(注意:シリアルポートはK210モジュールが接続されているポート番号を選択してください。不明な場合は、いったんPCのUSBケーブルをすべて抜き、K210モジュールだけをPCに接続してください。新たに追加されたポート番号が該当するポートです。)

接続が完了すると、下図のような接続成功のマークが表示されます。

(4)接続に成功したら、ツールバーの「ツール」をクリックし、「現在開いているスクリプトを(main.py)としてCanMV Camに保存」を選択します。

(5) 続いて「Yes」をクリックします。

(6)書き込みに成功するとメッセージが表示されるので、「OK」をクリックします。これでMicroPythonファイルがK210ビジョンモジュールに保存されます。
この方法で書き込んでおけば、以後はK210ビジョンモジュールに電源を供給するだけで(PCと接続しなくても)、モジュールがこのMicroPythonファイルを実行し、スタンドアロンで動作します。
- Type-CケーブルでSTM32に接続し、もう一端をPCに接続します。

- シリアルデバッグツールを開いて該当するポート番号を選択し、シリアルポートを開きます。AIモジュールで物体の画像を取得し、出力されるデータを確認します。
(ヒント:シリアルデバッグツールの使い方は、UNI-STM32開発環境の設定【V2.0】の「6. シリアルデバッグツールで結果を確認する」を参照してください。)
実験結果
物体の画像を取得すると、PCのシリアルデバッグツールに、認識された物体の中心位置や色などの情報が表示されます。
動画をクリックすると、動作の様子をご覧いただけます。
コード解説
1. ユーザーコードの解説
下図は、STM32プロジェクトファイルに追加したユーザーコードです。

2. ユーザーコードの取り込み方法
1つ目の方法は、ユーザー定義ファイル(.c や .h など)を自分のプロジェクトフォルダに取り込み、keil5 IDEで .c ファイルを追加してコンパイルする方法です。
既存のユーザーファイルの取り込み:keilで既存のユーザーファイルを取り込む方法 を参照してください。
2つ目の方法は、main.c ファイルにユーザーコードを追加する方法です。
ユーザーコード:/* USER CODE BEGIN xx で始まり、/* USER CODE END xx */ で終わる部分が目印です。
(1)まず参考プロジェクトのコードを開きます。main.c ファイルは主にSTM32CubeMXによって自動生成されたものです。このフレームワークをもとに実現したい機能を追加・補完します。具体的には /* USER xx で始まり、終わる部分のコードを確認してください。下図の赤で示した部分が追加したユーザーコードです。



(2)上記の赤で示したコードを自分のプロジェクトの main.c にコピーし、コンパイル・書き込みを行って動作を確認します。
技術サポート・お問い合わせ
資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。
ドキュメント

