STM32 連携による SPI-UART ゲートウェイ XJ3100 ドライバ設計

1. ハードウェア構成と接続概要

本デバイス(型番:XJ3100)は、標準的な SPI インターフェイスを介してシリアル通信データを転送する UART デバイスとして機能します。主に STM32 マイコンの GPIO を用いて外部デバイスを制御する際に使用されます。

開発環境には以下の主要コンポーネントが要求されます:

  • マイコンユニット:STM32F407(または同等の ARM Cortex-M コア)
  • 変換 IC:XJ3100(MAX3100 とピン互換)
  • 水晶発振器:3.6864MHz(推奨)

物理インタフェース定義

XJ3100 とマイコン間のデータ転送経路は以下の通り定義されます:

信号名 色識別 機能用 接続先
TX 黄色 UART 送信 相手機器 RX
MOSI 薄青 メインデバイスからの命令出力 マイコン MOSI
CLK クロックパルス マイコン SCK
CS 濃紺 チップセレクト(アクティブロー) マイコン GPIO

2. レジスタアーキテクチャ解説

XJ3100 の内部状態管理は、主に「設定レジスタ(Configuration)」と「データレジスタ(Data)」の 2 つの領域で構成されています。

データの書き込み・読み出しコマンドは、SPI 転送を行う際の MSB ビットによって判別されます。

  • 設定レジスタ操作: クロック動作モードや停止位、パーリティ設定などを変更するためにアクセスされます。
  • データレジスタ操作: 実際の UART データのバッファリングを行います。

3. ソフトウェア実装

以下に、C 言語を用いたドライバの実装例を示します。元のロジック構造を維持しつつ、可読性の高い関数名および変数名へ変更しています。

ヘッダファイル定義(xj3100_drv.h)

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html><html><body><pre><code>
#ifndef XJ3100_DRV_H_
#define XJ3100_DRV_H_

#include "stdint.h"

/* コマンドコード定義 */
#define OP_CODE_CONFIG_WR     (0xC0)
#define OP_CODE_CONFIG_RD     (0x40)
#define OP_CODE_DATA_WR       (0x80)
#define OP_CODE_DATA_RD       (0x00)

/* ビットフラグ定義(設定レジスタ用) */
#define MASK_R_FLAG           (0x8000)
#define MASK_T_FLAG           (0x4000)
#define MASK_FEN_EN           (0x2000) /* FIFO Enable */
#define MASK_SHDN_MODE        (0x1000)
#define MASK_TM_INT           (0x0800)
#define MASK_RM_INT           (0x0400)
#define MASK_PM_INT           (0x0200)
#define MASK_RAM_INT          (0x0100)
#define MASK_IR_MODE          (0x0080) /* IRDA Mode */
#define MASK_STP_POS          (0x0040) /* Stop Bit Pos */
#define MASK_PE_EN            (0x0020) /* Parity Enable */
#define MASK_LEN_BIT          (0x0010) /* Length Bit */
#define MASK_BAUD_MASK        (0x000F) /* Baudrate Select */

/* ビットフラグ定義(データレジスタ用) */
#define MASK_ERROR_RA_FE      (0x0400) /* Receive Active / Frame Error */
#define MASK_CTS_STATE        (0x0200)
#define MASK_PR_BIT           (0x0100) /* Parity Result */

/* 設定値定数 */
typedef enum {
    BAUD_RATE_600 = 0x0F,
    BAUD_RATE_1200,
    BAUD_RATE_2400,
    BAUD_RATE_4800,
    BAUD_RATE_9600,
    BAUD_RATE_19200,
    BAUD_RATE_38400,
    BAUD_RATE_76800,
    BAUD_RATE_115200,
    /* ...その他のボーレート定義は省略*/
    BAUD_RATE_MAX
} XJ3100_BaudRate_t;

/* 初期化処理 */
void XJ3100_Init(void);
void XJ3100_WriteReg(uint8_t reg_idx, uint16_t value);
uint16_t XJ3100_ReadReg(uint8_t reg_idx);

/* データ入出力 */
void XJ3100_SendByte(uint8_t data);
uint8_t XJ3100_RecvByte(void);

/* ステータス取得 */
bool XJ3100_CheckRxPending(void);
bool XJ3100_CheckTxEmpty(void);

#endif /* XJ3100_DRV_H_ */
</code></pre></body></html>

実装ソース(xj3100_drv.c)

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html><html><body><pre><code>
#include "xj3100_drv.h"
#include "spi_hal.h"
#include "gpio_hal.h"
#include "delay.h"

/* 専用 GPIO ピンの切り替え */
static void ChipSelect_Set(bool active) {
    if (active) {
        GPIO_Clear(CS_PIN);
    } else {
        GPIO_Set(CS_PIN);
    }
}

/**
 * @brief 設定レジスタへの書き込み
 */
void XJ3100_WriteConfig(uint8_t hi_byte, uint8_t lo_byte) {
    ChipSelect_Set(true);
    
    SPI_Transfer(OP_CODE_CONFIG_WR);
    SPI_Transfer(hi_byte);
    SPI_Transfer(lo_byte);
    
    ChipSelect_Set(false);
    Delay_ms(5);
}

/**
 * @brief 設定レジスタからの読み込み
 */
uint16_t XJ3100_ReadConfig() {
    uint8_t buf[2];
    ChipSelect_Set(true);
    
    SPI_Transfer(OP_CODE_CONFIG_RD); /* アドレス 0x40 */
    buf[0] = SPI_Transfer(0x00);
    buf[1] = SPI_Transfer(0x00);
    
    ChipSelect_Set(false);
    Delay_ms(5);
    
    return ((uint16_t)buf[0] << 8) | buf[1];
}

/**
 * @brief データレジスタへの書き込み
 */
void XJ3100_Send(uint8_t byte_val, bool enable_tx_enable_bit) {
    ChipSelect_Set(true);
    
    if (enable_tx_enable_bit) {
        SPI_Transfer(OP_CODE_DATA_WR | 0x04); 
    } else {
        SPI_Transfer(OP_CODE_DATA_WR);
    }
    SPI_Transfer(byte_val);
    
    ChipSelect_Set(false);
    Delay_ms(5);
}

/**
 * @brief データレジスタからの読み込み
 */
uint16_t XJ3100_ReceiveStatus() {
    uint8_t h_byte, l_byte;
    ChipSelect_Set(true);
    
    SPI_Transfer(OP_CODE_DATA_RD);
    h_byte = SPI_Transfer(0x00);
    l_byte = SPI_Transfer(0x00);
    
    ChipSelect_Set(false);
    Delay_ms(5);
    
    return ((uint16_t)h_byte << 8) | l_byte;
}

/**
 * @brief バージョンアップ対応のためのボールドレート設定
 */
void XJ3100_SetBaudrate(XJ3100_BaudRate_t rate) {
    uint16_t current = XJ3100_ReadConfig();
    current &= ~MASK_BAUD_MASK;
    current |= (uint16_t)rate;
    /* 上位ビットの保持のため、分割書き込みが必要となる場合もあるが簡略化 */
    XJ3100_WriteReg(0, current); 
}

/**
 * @brief FIFO エナブルフラグ設定
 * @param enabled True:有効,False:無効
 */
void XJ3100_EnableFIFO(bool enabled) {
    uint16_t reg = XJ3100_ReadConfig();
    if (enabled) {
        reg |= MASK_FEN_EN;
    } else {
        reg &= ~MASK_FEN_EN;
    }
    XJ3100_WriteReg(0, reg);
}

/**
 * @brief シュットダウンモード制御
 * @param shutdown True:シャットダウン,False:通常動作
 */
void XJ3100_ShutdownMode(bool shutdown) {
    uint16_t reg = XJ3100_ReadConfig();
    if (shutdown) {
        reg |= MASK_SHDN_MODE;
    } else {
        reg &= ~MASK_SHDN_MODE;
    }
    XJ3100_WriteReg(0, reg);
}

/**
 * @brief パリティチェック状態確認
 * @return 奇数ビット数時の結果 (0=偶,1=奇)
 */
uint8_t XJ3100_GetParityBit() {
    uint16_t stat = XJ3100_ReceiveStatus();
    if (stat & MASK_PR_BIT) return 1;
    return 0;
}

/**
 * @brief RTS リンク制御
 */
void XJ3100_ControlRTS(bool state) {
    // RTL の状態を保存し更新するロジック
    // 簡略化のためここではステートマシン的な挙動のみ示唆
}
</code></pre></body></html>

4. 運用上の留意点

通信速度の設定においては、水晶発振器の周波数が正しく駆動されていることを前提とし、レジスタ内のボーレート選定要領と一致しているか確認してください。また、データ伝送中は CS ラインを高レベルに保つことなく、適切なタイミングでイネーブする必要があります。

エラーハンドリングについては、フレームエラーや受信アクティブの状態を確認するフラグを定期的に poll することで検知可能です。

タグ: STM32 SPI UART embedded-drivers interface-protocol

6月28日 19:28 投稿