STM32のFSMC(flexible static memory controller)にLCDを接続する方法を調べました。
FSMCというのはSTM32の外部にSRAMやFLASHを接続するための機能です。FSMCを使ってパラレル接続のLCDを接続することができます。
この件については過去にいろんな人がいろんな記事を書いていますが、自分の理解のためにまとめてみます。
ターゲットのMCUはSTM32F103VCT6です。
officialの情報
- STM32のアプリケーションノート AN2790 「TFT LCD interfacing with the high-density STM32F10xxx FSMC」
- サンプルコード STM32016
- STM32F103VCT6のリファレンスマニュアル Rev16 英語
- [STM32F103VCT6のリファレンスマニュアル Rev11 日本語] (http://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/ja.CD00171190.pdf) バージョンが古いので注意
STM32F103VCT6 のFSMC
- STM32F103VCT6はLQFP100のパッケージでピンが足りないのでFSMCの信号が部分的にしか外ピンに出ていません。
- LQFP100で外ピンに出ているFSMCの信号
- D0-D15, A16-A23, CLK, NOE, NWE, NWAIT, NE1, NADV, NBL0, NBL1
- D0-D15はデータ、A16-A23はアドレス、NOEは負論理の出力イネーブル、NWEは負論理の書込みイネーブル、NE1はBANK1のチップセレクト
- CLK, NADV, NBL0, NBL1 はLCD接続する場合は使わない。
FSMCの設定
-
AN2790 のサンプルコード STM32016 のソースコードで行っている設定
-
FSMCタイミング設定 (バンク1のFSMC_BTR1レジスタ)
フィールド名 | 設定値 | 説明 |
---|---|---|
ACCMOD | 0x1 (アクセスモードB) | アクセスモード |
DATLAT | 0x0 (非同期SRAMの場合 don't care) | データ待ち時間 |
CLKDIV | 0x0 (非同期SRAMの場合 don't care) | クロック分周比 |
BUSTURN | 0x0 (1 × HCLK 周期) | バスターンアラウンドフェーズ時間 |
DATAST | 0x5 (6 × HCLK 周期) | データフェーズ時間 |
ADDHLD | 0x0 (1 × HCLK 周期) | アドレスホールドフェーズ時間 |
ADDSET | 0x1 (2 × HCLK 周期) | アドレスセットアップフェーズ時間 |
- SRAM/NOR 型フラッシュチップセレクト制御レジスタ設定 (バンク1のFSMC_BCR1レジスタ)
フィールド名 | 設定値 | 説明 |
---|---|---|
CBURSTRW | 0x0 (無効) | 書き込みバーストイネーブル |
ASYNCWAIT | 0x0 (考慮しない) | 非同期転送時のウェイト信号 |
EXTMOD | 0x0 (無効) | 拡張モードイネーブル |
WAITEN | 0x0 (無効) | ウェイトイネーブルビット |
WREN | 0x1 (有効) | 書き込みイネーブルビット |
WAITCFG | 0x0 (ウェイトステートの 1 データサイクル前にアクティブ) | ウェイトタイミング設定 |
WRAPMOD | 0x0 (無効) | ラップドバーストモードサポート |
WAITPOL | 0x0 (アクティブlow) | ウェイト信号極性ビット |
BURSTEN | 0x0 (無効) | バーストイネーブルビット |
FACCEN | 0x0 (無効) | フラッシュアクセスイネーブル |
MWID | 0x1 (16ビット) | メモリデータバス幅 |
MTYP | 0x0 (SRAM) | メモリタイプ |
MUXEN | 0x0 (多重化しない) | アドレス / データ多重化イネーブルビット 多重化しない |
MBKEN | 0x1 (有効) | メモリバンクイネーブルビット 有効 |
LCDとの接続
LCD | FSMC | STM32F103VCT6 |
---|---|---|
RESET | - | 60 PD12 |
RS | A16 | 58 PD11 |
CS | NE1 | 88 PD7 |
RD | NOE | 85 PD4 |
WR | NWE | 86 PD5 |
D0 | D0 | 61 PD14 |
D1 | D1 | 62 PD15 |
D2 | D2 | 81 PD0 |
D3 | D3 | 82 PD1 |
D4 | D4 | 38 PE7 |
D5 | D5 | 39 PE8 |
D6 | D6 | 40 PE9 |
D7 | D7 | 41 PE10 |
D8 | D8 | 42 PE11 |
D9 | D9 | 43 PE12 |
D10 | D10 | 44 PE13 |
D11 | D11 | 45 PE14 |
D12 | D12 | 46 PE15 |
D13 | D13 | 55 PD8 |
D14 | D14 | 56 PD9 |
D15 | D15 | 57 PD10 |
FSMCの初期設定例
- STM32F103 で libopencm3 をライブラリとして使った場合のFSMC初期設定例
# include <libopencm3/stm32/rcc.h>
# include <libopencm3/stm32/gpio.h>
# include <libopencm3/stm32/fsmc.h>
static void fsmc_setup(void)
{
/* Enable PORTD and PORTE */
rcc_periph_clock_enable(RCC_GPIOD);
rcc_periph_clock_enable(RCC_GPIOE);
/* Enable FSMC */
rcc_periph_clock_enable(RCC_FSMC);
/* config FSMC data lines */
uint16_t portd_gpios = GPIO0 | GPIO1 | GPIO8 | GPIO9 |
GPIO10 | GPIO14 | GPIO15;
gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, portd_gpios);
uint16_t porte_gpios = GPIO7 | GPIO8 | GPIO9 | GPIO10 |
GPIO11 | GPIO12 | GPIO13 | GPIO14 |
GPIO15;
gpio_set_mode(GPIOE, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, porte_gpios);
/* config FSMC NOE */
gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO4);
/* config FSMC NWE */
gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO5);
/* config FSMC NE1 */
gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO7);
/* config FSMC A16 for D/C (select Data / Command ) */
gpio_set_mode(GPIOD, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO11);
/* config FSMC register */
FSMC_BTR(0) = FSMC_BTR_ACCMODx(FSMC_BTx_ACCMOD_B) |
FSMC_BTR_DATLATx(0) |
FSMC_BTR_CLKDIVx(0) |
FSMC_BTR_BUSTURNx(0) |
FSMC_BTR_DATASTx(5) |
FSMC_BTR_ADDHLDx(0) |
FSMC_BTR_ADDSETx(1);
FSMC_BCR(0) = FSMC_BCR_WREN | FSMC_BCR_MWID | FSMC_BCR_MBKEN;
}
LCDコントローラへのアクセス
- LCDアクセス用構造体
- 上記の接続でRSピンの出力が0と1になるように構造体LCDを0x6001FFFEに配置する。
- LCD->LCD_REG でアクセスするとA16が0になる。(アドレス 0x6001FFFE)
- LCD->LCD_RAM でアクセスするとA16が1になる。(アドレス 0x60020000)
/* Private typedef -----------------------------------------------------------*/
typedef struct
{
__IO uint16_t LCD_REG;
__IO uint16_t LCD_RAM;
} LCD_TypeDef;
# define LCD_BASE ((uint32_t)(0x60000000 | 0x00020000 -2 ) )
# define LCD ((LCD_TypeDef *) LCD_BASE)
- レジスタの読み出し
uint16_t LCD_ReadReg(uint8_t LCD_Reg)
{
/* Write 16-bit Index (then Read Reg) */
LCD->LCD_REG = LCD_Reg;
/* Read 16-bit Reg */
return (LCD->LCD_RAM);
}
- レジスタへの書込み
void LCD_WriteReg(uint8_t LCD_Reg, uint16_t LCD_RegValue)
{
/* Write 16-bit Index, then Write Reg */
LCD->LCD_REG = LCD_Reg;
/* Write 16-bit Reg */
LCD->LCD_RAM = LCD_RegValue;
}
- RAMの読み出し
- RAMの読み出しは、コマンドが必要なので、使用するコントローラ毎に異なる。
uint16_t LCD_ReadRAM(void)
{
/* Write 16-bit Index (then Read Reg) */
LCD->LCD_REG = R34 /* Select GRAM Reg */
/* Read 16-bit Reg */
return LCD->LCD_RAM;
}
- RAMへの書込み
void LCD_WriteRAM(uint16_t RGB_Code)
{
/* Write 16-bit GRAM Reg */
LCD->LCD_RAM = RGB_Code;
}