はじめに
前回「STM32C011F4P6でNFCタグを読む(M5Stack Unit RFID2)」 の続きです。
今回は、STM32C011F4P6とM5Stack Unit RFID2、DFPlayer miniを使って
読み取ったNTAG215内の数字に対応した音楽ファイルを再生します。
また電子工作初心者ですので、誤ったことを記載している可能性があります。
ご了承ください。
ブロック図
部品, 備品類
部品
AE-STM32C011F4P6-DIP(秋月電子キット)
Nucleo Board STM32C031C6
ブレッドボード(BB-801を使っています)
ジャンパーワイヤー オス-オス
ジャンパーワイヤー オス-メス
ピンヘッダ
USB A-マイクロB(2.0) (Nucleoボード接続用)
M5Stack用WS1850S搭載 RFID 2ユニット, 型番:M5STACK-U031-B(スイッチサイエンス)
NTAG215(AmazonのYFFSFDC NFCタグ NTAG215 30枚)
タクトスイッチ
DFPlayer mini (DFR0299)
マイクロSDカード
マイクロスピーカー 青/白リード付 8Ω(秋月電子)
備品, ソフト
パソコン(Windows11を使いました)
はんだごて、はんだ
STM32CubeMX
STM32CubeIDE
NFC Tools(iPhone版を使いました)
できたもの
NTAG215をRFID2にかざすと、対応したmp3が再生されます。
(NTAG:001 >> 0001.mp3が再生)
DFPlayer miniの動作確認
マイクロSDカードへ音楽ファイルの保管
下記ファイル構成で、mp3ファイルを保管します。
SDカード
│
├─mp3
├─0001.mp3
├─0002.mp3
音楽ファイルは、下記リンク先のものを使わせていただきました。
効果音ラボ
https://soundeffect-lab.info/sound/
Springin' Sound Stock
https://www.springin.org/sound-stock/
DFPlayer Miniの配線
下図のように配線しています。
S3, S4はなし
VCCはnucleoボードの5V給電
GNDもnucleoボードのGND
マイクロSDは忘れずにセットしてください。
操作方法は下記です。
IO1:短く押す:次の曲再生/ 長く押す:音量低下
IO2:短く押す:前の曲再生/ 長く押す:音量増加
mp3を再生できることが分かったため、次に移ります。
ピン設定等の初期設定、コード生成
STM32CubeMXを起動します。
File → New Project をクリック。
ウインドウが表示されますので使用するマイコンを「Commercial Part Number」に入力します。(今回はSTM32C011F4P6)
MCUs/MCUs Listから選択 → Start Project をクリック。
ピンの設定画面が表示されますので、ピン設定を行います。
PB7:I2C1_SCL
PC14:I2C1_SDA
PA0:USART1_TX
PA1:USART1_RX
PA2:USART2_TX
PA3:USART2_RX
I2C:RFID2用、USART1:RFID2デバッグ用、USART2:DF Player mini用
I2CとUSART1の設定は、前回「STM32C011F4P6でNFCタグを読む(M5Stack Unit RFID2)」 同様に設定します。
USART2の設定
左メニューの「Connectivity」→「USART2」→Modeを「Asynchronous」へ変更します。
Baud Rateを9600bpsに変更します。(データシート記載のデフォルト)
「Project Manager」タブを押し、
「Project Name」にファイル名を記入
「Project Location」にて保存場所を選択
「Toolchain /IDE」をSTM32CubeIDEを選択
「GENERATE CODE」を押し、コード生成
※プロジェクトネームに"()"を使うと、ビルド時にエラーが発生するため使わないでください。
※プロジェクトロケーションは「CubeMX」直下としてください。
「CubeMX\プロジェクトネーム」とした場合、CubeIDEでエラーが発生します。
※画像は1回目のものです。
補足:USART2左側の黄色三角警告について
USART2の左に黄色三角警告があります。
調べると、制限付きで使用可能のモードのようです。
CubeMXのSystem viewにてアラートはないため、このまま進めます。
STM32G0 -STM32CubeMXより
https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32G0/STM32G0-Ecosystem-STM32CubeMX-Tool-J-.pdf
DF Player miniとSTM32の配線
前回「STM32C011F4P6でNFCタグを読む(M5Stack Unit RFID2)」 の配線に、
下記の配線を追加します。
DFplayer mini - マイコン(STM32C011F4P6)
pin2(RX) - 1kohm抵抗 - pin9(PA2)
pin2(TX) - pin10(PA3)
DFPlayerのRXには 1kΩ抵抗を直列に入れてください(3.3V信号を5V系DFPlayerに入力するための保護)
DFplayer mini - STLINK
VCC - 5V(5V給電)
DFplayer mini - 共通
pin7(GND), pin10(GND) - GND
下記投稿によると、GND2ピン落とすと音質が良くなるとのこと。
Issue with DFPlayer Mini and Raspberry Pi Pico - Only Hissing Sound
https://www.reddit.com/r/raspberrypipico/comments/1gxy6jh/issue_with_dfplayer_mini_and_raspberry_pi_pico/?show=original
分かりづらいのですが、参考図が下記です。
TXとRXは入れ替えて接続します。(デバッガTX - マイコンRX)
コード入力、ビルド
STMCubeIDEのインポートしたプロジェクト内「Core」 → 「Src」 → 「main.c」にコードを記入します。
コードは下記範囲内にコピペしてください。
/* USER CODE BEGIN Header /
コードのコピペ
/ USER CODE END 3 */
NTAG215+DFPlayerMini
/* USER CODE BEGIN Header */
/*
* STM32 Port of M5Stack RFID2 / MFRC522_I2C functionality
*
* This code is released under the Creative Commons Public Domain Dedication
* version 1.0 or later (CC0 1+).
* To the extent possible under law, the author has waived all copyright
* and related or neighboring rights to this work.
* You may copy, modify, distribute and perform the work, even for commercial
* purposes, all without asking permission.
*
* -------------------------------------------------------------------------
* Third-party components and licenses
* -------------------------------------------------------------------------
*
* MFRC522_I2C Library (arozcan)
* - Public Domain
* - https://github.com/arozcan/MFRC522-I2C-Library
*
* M5Unified (M5Stack)
* - MIT License
* - Copyright (c) M5Stack
* - https://github.com/m5stack/M5Unified
*
* M5Stack Unit RFID / RFID2 documentation
* - No explicit license noted in documentation
* - Uses official M5Stack libraries (MIT License)
* - https://docs.m5stack.com/ja/arduino/projects/unit/unit_rfid
*
* MIT License portions are used in accordance with their terms.
*
*/
/**
******************************************************************************
* @file : main.c
* @brief : NTAG215 テキスト(3桁数字) 読み取り + DFPlayer Mini 再生 v12
* STM32C011F4P6 + M5Stack RFID2 Unit (WS1850S) + DFPlayer Mini
*
* 【v11 → v12 変更点】
* [1] 同一カード連続再生スキップを廃止 → 毎回タッチで再生
* [2] DFPlayerコマンドを 0x0F → 0x12 に修正
* 0x12: /mp3/フォルダの4桁ファイル番号指定再生
* 例: num=1 → /mp3/0001.mp3, num=2 → /mp3/0002.mp3
* [3] 対応ファイル番号範囲を 1〜9999 に拡大 (999個運用可能)
* [4] 不要な変数・define を削除してFLASH節約
*
* 【SDカード内のフォルダ/ファイル構成】
* FAT32フォーマットのSDカード直下に /mp3/ フォルダを作成:
* /mp3/0001.mp3 ← NTAGに"001"と書いた場合に再生
* /mp3/0002.mp3 ← NTAGに"002"と書いた場合に再生
* /mp3/0003.mp3 ← NTAGに"003"と書いた場合に再生
* ... ← 0999.mp3まで追加可能
*
* 【配線まとめ】
* RFID2 SCL → PB7 (I2C1_SCL)
* RFID2 SDA → PC14 (I2C1_SDA)
* RFID2 VCC → 3.3V / GND → GND
* ST-LINK → PA0(USART1_TX) / PA1(USART1_RX) ← デバッグ用
* DFPlayer TX → PA3 (USART2_RX)
* DFPlayer RX → PA2 (USART2_TX) ← 1kΩ直列推奨
* DFPlayer VCC → 5V / GND → GND
* DFPlayer SPK_1/SPK_2 → 8Ω スピーカー
*
* 【STM32CubeMX ピン設定】
* PB7 : I2C1_SCL
* PC14 : I2C1_SDA
* PA0 : USART1_TX (115200bps, デバッグ)
* PA1 : USART1_RX
* PA2 : USART2_TX (9600bps, DFPlayer)
* PA3 : USART2_RX
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum {
MFRC_OK = 0,
MFRC_ERROR = 1,
MFRC_TIMEOUT = 2
} MFRC_Status;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* I2C アドレス (7bit=0x28 → HAL用8bit) */
#define RFID2_ADDR (0x28 << 1)
/* ---- WS1850S (MFRC522互換) レジスタ ---- */
#define REG_CommandReg 0x01
#define REG_ComIrqReg 0x04
#define REG_ErrorReg 0x06
#define REG_FIFODataReg 0x09
#define REG_FIFOLevelReg 0x0A
#define REG_BitFramingReg 0x0D
#define REG_CollReg 0x0E
#define REG_ModeReg 0x11
#define REG_TxControlReg 0x14
#define REG_TxASKReg 0x15
#define REG_RFCfgReg 0x26
#define REG_CRCResultRegH 0x21
#define REG_CRCResultRegL 0x22
#define REG_TModeReg 0x2A
#define REG_TPrescalerReg 0x2B
#define REG_TReloadRegH 0x2C
#define REG_TReloadRegL 0x2D
/* ---- コマンド ---- */
#define CMD_Idle 0x00
#define CMD_CalcCRC 0x03
#define CMD_Transceive 0x0C
#define CMD_SoftReset 0x0F
/* ---- NFC コマンド ---- */
#define PICC_REQA 0x26
#define PICC_CT 0x88
#define PICC_SEL_CL1 0x93
#define PICC_SEL_CL2 0x95
#define PICC_HLTA 0x50
#define NTAG_READ 0x30
/* ErrorReg マスク */
#define ERR_MASK_STRICT 0x13
#define ERR_MASK_ANTICOLL 0x13
/* 待機時間 (ms) */
#define WAIT_MS_REQA 15u
#define WAIT_MS_ANTICOLL 25u
#define WAIT_MS_SELECT 30u
#define WAIT_MS_READ 30u
#define WAIT_MS_CRC 5u
/* デバッグ出力: 0=OFF, 1=ON */
#define RFID_DEBUG 0
/* ---- DFPlayer Mini コマンド定数 ---- */
#define DFP_START 0x7E
#define DFP_VERSION 0xFF
#define DFP_LEN 0x06
#define DFP_END 0xEF
#define DFP_FEEDBACK 0x00 /* ACK不要 */
#define DFP_CMD_VOLUME 0x06 /* 音量設定 */
#define DFP_CMD_RESET 0x0C /* リセット */
#define DFP_CMD_PLAY_MP3 0x12 /* /mp3/フォルダ 4桁ファイル番号再生 */
#define DFP_VOLUME_DEFAULT 20 /* 音量 0〜30 */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#if RFID_DEBUG
#define DBG_STR(s) uart_str(s)
#define DBG_HEX8(v) uart_hex8(v)
#define DBG_CRLF() uart_crlf()
#else
#define DBG_STR(s) ((void)0)
#define DBG_HEX8(v) ((void)0)
#define DBG_CRLF() ((void)0)
#endif
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* UART1 ユーティリティ (デバッグ用) */
static void uart_str(const char *s);
static void uart_hex8(uint8_t v);
static void uart_crlf(void);
/* WS1850S ドライバ */
static void mfrc_write(uint8_t reg, uint8_t val);
static uint8_t mfrc_read(uint8_t reg);
static void mfrc_read_burst(uint8_t reg, uint8_t *buf, uint8_t len);
static void mfrc_set_bits(uint8_t reg, uint8_t mask);
static void mfrc_clear_bits(uint8_t reg, uint8_t mask);
static void mfrc_fifo_flush(void);
static void mfrc_init(void);
static MFRC_Status mfrc_transceive(uint8_t *tx, uint8_t txLen,
uint8_t *rx, uint8_t *rxLen,
uint8_t errMask, uint32_t waitMs);
static MFRC_Status mfrc_calc_crc(uint8_t *data, uint8_t len,
uint8_t *crcLow, uint8_t *crcHigh);
/* NTAG215 操作 */
static MFRC_Status ntag_request(void);
static MFRC_Status ntag_select_7byte(uint8_t *uid7);
static MFRC_Status ntag_read_page(uint8_t page, uint8_t *data16);
static uint8_t extract_3digit(uint8_t *raw, uint8_t len, char *out3);
/* DFPlayer Mini */
static void dfp_send_cmd(uint8_t cmd, uint8_t param1, uint8_t param2);
static void dfp_init(void);
static void dfp_play_number(uint16_t num);
/* メインタスク */
static void rfid_task(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* ================================================================
* UART1 ユーティリティ (デバッグ用・stdio.h不使用)
* ================================================================ */
static void uart_str(const char *s)
{
while (*s)
HAL_UART_Transmit(&huart1, (uint8_t *)s++, 1, 100);
}
static void uart_hex8(uint8_t v)
{
const char h[] = "0123456789ABCDEF";
uint8_t buf[2];
buf[0] = (uint8_t)h[(v >> 4) & 0x0F];
buf[1] = (uint8_t)h[v & 0x0F];
HAL_UART_Transmit(&huart1, buf, 2, 100);
}
static void uart_crlf(void) { uart_str("\r\n"); }
/* ================================================================
* WS1850S I2C ドライバ
* ================================================================ */
static void mfrc_write(uint8_t reg, uint8_t val)
{
uint8_t buf[2] = { reg, val };
HAL_I2C_Master_Transmit(&hi2c1, RFID2_ADDR, buf, 2, 100);
}
static uint8_t mfrc_read(uint8_t reg)
{
uint8_t val = 0;
HAL_I2C_Master_Transmit(&hi2c1, RFID2_ADDR, ®, 1, 100);
HAL_I2C_Master_Receive (&hi2c1, RFID2_ADDR, &val, 1, 100);
return val;
}
static void mfrc_read_burst(uint8_t reg, uint8_t *buf, uint8_t len)
{
HAL_I2C_Master_Transmit(&hi2c1, RFID2_ADDR, ®, 1, 100);
HAL_I2C_Master_Receive (&hi2c1, RFID2_ADDR, buf, len, 100);
}
static void mfrc_set_bits(uint8_t reg, uint8_t mask)
{
mfrc_write(reg, mfrc_read(reg) | mask);
}
static void mfrc_clear_bits(uint8_t reg, uint8_t mask)
{
mfrc_write(reg, mfrc_read(reg) & ~mask);
}
static void mfrc_fifo_flush(void)
{
mfrc_write(REG_FIFOLevelReg, 0x80);
HAL_Delay(1);
}
static void mfrc_init(void)
{
mfrc_write(REG_CommandReg, CMD_SoftReset);
HAL_Delay(100);
mfrc_write(REG_TModeReg, 0x80);
mfrc_write(REG_TPrescalerReg, 0xA9);
mfrc_write(REG_TReloadRegH, 0x03);
mfrc_write(REG_TReloadRegL, 0xE8);
mfrc_write(REG_TxASKReg, 0x40);
mfrc_write(REG_ModeReg, 0x3D);
mfrc_set_bits(REG_RFCfgReg, 0x70); /* アンテナゲイン最大 */
mfrc_set_bits(REG_TxControlReg, 0x03); /* アンテナON */
mfrc_write(REG_BitFramingReg, 0x00);
}
/* ================================================================
* mfrc_transceive()
* 【重要】送信順序:
* ① CommandReg = CMD_Transceive (コマンド先)
* ② BitFramingReg = 0x80 (StartSend後)
* ================================================================ */
static MFRC_Status mfrc_transceive(uint8_t *tx, uint8_t txLen,
uint8_t *rx, uint8_t *rxLen,
uint8_t errMask, uint32_t waitMs)
{
uint8_t i, n, err;
mfrc_fifo_flush();
mfrc_write(REG_ComIrqReg, 0x7F);
mfrc_write(REG_CommandReg, CMD_Idle);
mfrc_write(REG_BitFramingReg, 0x00);
for (i = 0; i < txLen; i++)
mfrc_write(REG_FIFODataReg, tx[i]);
mfrc_write(REG_CommandReg, CMD_Transceive); /* ①コマンド先 */
mfrc_write(REG_BitFramingReg, 0x80); /* ②StartSend後 */
HAL_Delay(waitMs);
mfrc_write(REG_BitFramingReg, 0x00);
(void)mfrc_read(REG_ComIrqReg); /* irq読み捨て (未使用) */
err = mfrc_read(REG_ErrorReg);
n = mfrc_read(REG_FIFOLevelReg);
DBG_STR(" E=0x"); DBG_HEX8(err);
DBG_STR(" F="); DBG_HEX8(n); DBG_CRLF();
if (err & errMask) return MFRC_ERROR;
if (n == 0) return MFRC_TIMEOUT;
if (rx && rxLen) {
uint8_t lim = (*rxLen < n) ? *rxLen : n;
mfrc_read_burst(REG_FIFODataReg, rx, lim);
*rxLen = lim;
DBG_STR(" RX["); DBG_HEX8(lim); DBG_STR("]:");
for (i = 0; i < lim; i++) { DBG_STR(" "); DBG_HEX8(rx[i]); }
DBG_CRLF();
}
return MFRC_OK;
}
/* ================================================================
* mfrc_calc_crc() : ハードウェアCRC_A計算
* ================================================================ */
static MFRC_Status mfrc_calc_crc(uint8_t *data, uint8_t len,
uint8_t *crcLow, uint8_t *crcHigh)
{
uint8_t i;
mfrc_write(REG_CommandReg, CMD_Idle);
mfrc_write(REG_ComIrqReg, 0x04);
mfrc_fifo_flush();
for (i = 0; i < len; i++)
mfrc_write(REG_FIFODataReg, data[i]);
mfrc_write(REG_CommandReg, CMD_CalcCRC);
HAL_Delay(WAIT_MS_CRC);
mfrc_write(REG_CommandReg, CMD_Idle);
if (!(mfrc_read(REG_ComIrqReg) & 0x04)) return MFRC_TIMEOUT;
*crcLow = mfrc_read(REG_CRCResultRegL);
*crcHigh = mfrc_read(REG_CRCResultRegH);
return MFRC_OK;
}
/* ================================================================
* ntag_request() : REQA 7bit送信
* ================================================================ */
static MFRC_Status ntag_request(void)
{
uint8_t cmd = PICC_REQA;
uint8_t rx[2];
uint8_t n, err;
mfrc_fifo_flush();
mfrc_write(REG_ComIrqReg, 0x7F);
mfrc_write(REG_CommandReg, CMD_Idle);
mfrc_write(REG_BitFramingReg, 0x00);
mfrc_write(REG_FIFODataReg, cmd);
mfrc_write(REG_CommandReg, CMD_Transceive);
mfrc_write(REG_BitFramingReg, 0x87); /* StartSend + TxLastBits=7 */
HAL_Delay(WAIT_MS_REQA);
mfrc_write(REG_BitFramingReg, 0x00);
err = mfrc_read(REG_ErrorReg);
n = mfrc_read(REG_FIFOLevelReg);
if (err & ERR_MASK_ANTICOLL) return MFRC_ERROR;
if (n == 0) return MFRC_TIMEOUT;
mfrc_read_burst(REG_FIFODataReg, rx, (n > 2) ? 2 : n);
return MFRC_OK;
}
/* ================================================================
* ntag_select_7byte() : Cascade Level 1+2 で7バイトUID取得
* ================================================================ */
static MFRC_Status ntag_select_7byte(uint8_t *uid7)
{
uint8_t rx[5];
uint8_t rxLen;
uint8_t tx[9];
uint8_t bcc;
uint8_t crcL, crcH;
MFRC_Status st;
/* ---- Cascade Level 1 ---- */
mfrc_clear_bits(REG_CollReg, 0x80);
tx[0] = PICC_SEL_CL1; tx[1] = 0x20;
rxLen = sizeof(rx);
st = mfrc_transceive(tx, 2, rx, &rxLen, ERR_MASK_ANTICOLL, WAIT_MS_ANTICOLL);
if (st != MFRC_OK || rxLen < 5 || rx[0] != PICC_CT) return MFRC_ERROR;
bcc = rx[0] ^ rx[1] ^ rx[2] ^ rx[3];
tx[0] = PICC_SEL_CL1; tx[1] = 0x70;
tx[2] = rx[0]; tx[3] = rx[1]; tx[4] = rx[2]; tx[5] = rx[3]; tx[6] = bcc;
if (mfrc_calc_crc(tx, 7, &crcL, &crcH) != MFRC_OK) return MFRC_ERROR;
tx[7] = crcL; tx[8] = crcH;
rxLen = 3;
st = mfrc_transceive(tx, 9, rx, &rxLen, ERR_MASK_STRICT, WAIT_MS_SELECT);
if (st != MFRC_OK) return MFRC_ERROR;
uid7[0] = tx[3]; uid7[1] = tx[4]; uid7[2] = tx[5];
/* ---- Cascade Level 2 ---- */
mfrc_clear_bits(REG_CollReg, 0x80);
tx[0] = PICC_SEL_CL2; tx[1] = 0x20;
rxLen = sizeof(rx);
st = mfrc_transceive(tx, 2, rx, &rxLen, ERR_MASK_ANTICOLL, WAIT_MS_ANTICOLL);
if (st != MFRC_OK || rxLen < 5) return MFRC_ERROR;
bcc = rx[0] ^ rx[1] ^ rx[2] ^ rx[3];
tx[0] = PICC_SEL_CL2; tx[1] = 0x70;
tx[2] = rx[0]; tx[3] = rx[1]; tx[4] = rx[2]; tx[5] = rx[3]; tx[6] = bcc;
if (mfrc_calc_crc(tx, 7, &crcL, &crcH) != MFRC_OK) return MFRC_ERROR;
tx[7] = crcL; tx[8] = crcH;
rxLen = 3;
st = mfrc_transceive(tx, 9, rx, &rxLen, ERR_MASK_STRICT, WAIT_MS_SELECT);
if (st != MFRC_OK) return MFRC_ERROR;
uid7[3] = tx[2]; uid7[4] = tx[3]; uid7[5] = tx[4]; uid7[6] = tx[5];
return MFRC_OK;
}
/* ================================================================
* ntag_read_page() : NTAGページ読み取り (4ページ=16byte返却)
* ================================================================ */
static MFRC_Status ntag_read_page(uint8_t page, uint8_t *data16)
{
uint8_t tx[4];
uint8_t buf[18]; /* 16データ + CRC2バイト */
uint8_t rxLen = sizeof(buf);
uint8_t crcL, crcH;
uint8_t i;
MFRC_Status st;
tx[0] = NTAG_READ; tx[1] = page;
if (mfrc_calc_crc(tx, 2, &crcL, &crcH) != MFRC_OK) return MFRC_ERROR;
tx[2] = crcL; tx[3] = crcH;
st = mfrc_transceive(tx, 4, buf, &rxLen, ERR_MASK_STRICT, WAIT_MS_READ);
if (st != MFRC_OK) return st;
for (i = 0; i < 16 && i < rxLen; i++)
data16[i] = buf[i];
return MFRC_OK;
}
/* ================================================================
* extract_3digit() : raw[len]からASCII数字3連続を検索
* ================================================================ */
static uint8_t extract_3digit(uint8_t *raw, uint8_t len, char *out3)
{
uint8_t i;
for (i = 0; i + 2 < len; i++) {
if (raw[i] >= '0' && raw[i] <= '9' &&
raw[i+1] >= '0' && raw[i+1] <= '9' &&
raw[i+2] >= '0' && raw[i+2] <= '9') {
out3[0] = (char)raw[i];
out3[1] = (char)raw[i+1];
out3[2] = (char)raw[i+2];
out3[3] = '\0';
return 1;
}
}
return 0;
}
/* ================================================================
* DFPlayer Mini ドライバ
*
* コマンドフォーマット (10バイト):
* [START] [VER] [LEN] [CMD] [FB] [P1] [P2] [CHK_H] [CHK_L] [END]
* 0x7E 0xFF 0x06 ... 0x00 ... ... ... ... 0xEF
*
* チェックサム = -(VER + LEN + CMD + FB + P1 + P2)
*
* コマンド 0x12 (/mp3/フォルダ再生):
* P1 = ファイル番号上位バイト
* P2 = ファイル番号下位バイト
* 例: num=1 → P1=0x00, P2=0x01 → /mp3/0001.mp3
* num=2 → P1=0x00, P2=0x02 → /mp3/0002.mp3
* num=999→ P1=0x03, P2=0xE7 → /mp3/0999.mp3
* ================================================================ */
static void dfp_send_cmd(uint8_t cmd, uint8_t param1, uint8_t param2)
{
uint8_t buf[10];
int16_t checksum;
buf[0] = DFP_START;
buf[1] = DFP_VERSION;
buf[2] = DFP_LEN;
buf[3] = cmd;
buf[4] = DFP_FEEDBACK;
buf[5] = param1;
buf[6] = param2;
checksum = -(int16_t)(buf[1] + buf[2] + buf[3] + buf[4] + buf[5] + buf[6]);
buf[7] = (uint8_t)((checksum >> 8) & 0xFF);
buf[8] = (uint8_t)(checksum & 0xFF);
buf[9] = DFP_END;
HAL_UART_Transmit(&huart2, buf, 10, 200);
}
static void dfp_init(void)
{
HAL_Delay(1000); /* DFPlayer 起動待ち */
dfp_send_cmd(DFP_CMD_RESET, 0, 0);
HAL_Delay(1500); /* リセット後の初期化待ち */
dfp_send_cmd(DFP_CMD_VOLUME, 0, DFP_VOLUME_DEFAULT);
HAL_Delay(100);
}
/* ================================================================
* dfp_play_number()
* num : 1〜9999
* コマンド 0x12 で /mp3/XXXX.mp3 を再生
* NTAGの3桁数字 "001"〜"999" → num=1〜999 → 0001.mp3〜0999.mp3
* ================================================================ */
static void dfp_play_number(uint16_t num)
{
if (num < 1 || num > 9999) {
uart_str("DFP: num out of range\r\n");
return;
}
/* 0x12: P1=上位バイト, P2=下位バイト */
dfp_send_cmd(DFP_CMD_PLAY_MP3, (uint8_t)(num >> 8), (uint8_t)(num & 0xFF));
uart_str("DFP: play /mp3/");
uart_hex8((uint8_t)(num >> 8));
uart_hex8((uint8_t)(num & 0xFF));
uart_crlf();
}
/* ================================================================
* rfid_task() : メインループから定期呼び出し
*
* 【v12】同一カードスキップなし → タッチするたびに毎回再生
* ================================================================ */
static void rfid_task(void)
{
uint8_t uid[7], page_data[16], raw[48];
char digit3[4];
uint8_t p, b;
/* カード検出 */
if (ntag_request() != MFRC_OK) return;
/* 7バイトUID取得 (Cascade Level 1+2) */
if (ntag_select_7byte(uid) != MFRC_OK) {
uart_str("SEL NG\r\n");
return;
}
/* UID表示 */
uart_str("UID:");
for (b = 0; b < 7; b++) { uart_hex8(uid[b]); uart_str(" "); }
uart_crlf();
/* page4〜6 読み取り (3ページ × 16byte = 48byte) */
for (p = 0; p < 3; p++) {
if (ntag_read_page(4 + p, page_data) != MFRC_OK) {
uart_str("RD NG\r\n");
goto halt_card;
}
for (b = 0; b < 16; b++)
raw[p * 16 + b] = page_data[b];
}
/* 3桁数字抽出・DFPlayer再生 */
if (extract_3digit(raw, 48, digit3)) {
uint16_t num;
uart_str(">>>"); uart_str(digit3); uart_str("<<<\r\n");
num = (uint16_t)((digit3[0] - '0') * 100
+ (digit3[1] - '0') * 10
+ (digit3[2] - '0'));
if (num >= 1) {
dfp_play_number(num); /* 毎回タッチで再生 */
} else {
uart_str("DFP: skip (num=0)\r\n");
}
} else {
uart_str("NO 3DIGIT\r\n");
}
halt_card:
/* HALT送信 */
{
uint8_t halt_tx[4], dummy[2];
uint8_t dLen = 2, crcL, crcH;
halt_tx[0] = PICC_HLTA; halt_tx[1] = 0x00;
mfrc_calc_crc(halt_tx, 2, &crcL, &crcH);
halt_tx[2] = crcL; halt_tx[3] = crcH;
mfrc_transceive(halt_tx, 4, dummy, &dLen,
ERR_MASK_ANTICOLL, WAIT_MS_SELECT);
}
HAL_Delay(1000); /* 連続読み取り防止のための最小待機 */
}
/* USER CODE END 0 */
/* ================================================================
* main()
* ================================================================ */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
HAL_Delay(500);
uart_str("RFID+DFPlayer v12\r\n");
mfrc_init();
uart_str("RFID Ready\r\n");
dfp_init();
uart_str("DFPlayer Ready\r\n");
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
rfid_task();
HAL_Delay(300);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
コードはClaude Sonnet4.6に書いてもらいました。
メニューの「Project」 → 「 Build All」 を押しコンパイル&ビルドします。
※1回目の画像です。
問題がなければ、下部のコンソール画面に「0errors, 0warnings」と表示されます。
※1回目の画像です。
動作確認(デバッグ)
NucleoボードとパソコンをUSBで接続します。(接続中の場合はそのまま)
「Run」 → 「Debug」をクリック
デバッグ画面に切り替えを提案されるため、
「Switch」を押して了承する。
「
」Resumeボタンをクリック
NTAGをRFID2にかざすと、[できたもの(https://elchika.com/article/e743071e-a789-4c5d-ae84-e8a5c14e05eb/#h_できたもの) の通り、
対応したmp3ファイルが再生されます。
STM32 Cube IDEのシリアルモニターは下記表示になります。
プログラムメモリ16kBに対し、15.65kBと結構使っています。
参考リンク
使わせていただいた音源
効果音ラボ
https://soundeffect-lab.info/sound/
Springin' Sound Stock
https://www.springin.org/sound-stock/
CubeMX解説 (ピン割り当て時、警告の説明)
STMicroelectronics, STM32G0 -STM32CubeMX
https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32G0/STM32G0-Ecosystem-STM32CubeMX-Tool-J-.pdf
DFPlayer miniの使い方
DFROBOT, DFPlayer Mini Mp3 Player - DFRobot Wiki
https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299
秋月電子通商, [112544]DFPlayer mini (MP3プレーヤー)
https://akizukidenshi.com/catalog/g/g112544/
創造技術実習, DFPlayer – A Mini MP3 Playerを使う
https://robo164.com/archives/426
reddit, Issue with DFPlayer Mini and Raspberry Pi Pico - Only Hissing Sound
https://www.reddit.com/r/raspberrypipico/comments/1gxy6jh/issue_with_dfplayer_mini_and_raspberry_pi_pico/?tl=ja
投稿者の人気記事




-
OY
さんが
前の火曜日の0:55
に
編集
をしました。
(メッセージ: 初版)
-
OY
さんが
前の火曜日の0:56
に
編集
をしました。
ログインしてコメントを投稿する