SPI はSerial Peripheral Interface (Serial Peripheral Interface) の略称で、高速、全二重、同期通信バスであり、チップのピン上の 4 本のワイヤのみを占有します。
IIC と同様、マイクロコントローラー開発で最も一般的に使用される通信方式の 1 つです。IICと比較すると以下のような特徴があります。
1. 通信速度が速い STM32F103C8T6 を例にとると、SPI 通信速度は 18Mbps、つまり 2.25MB/s に達しますが、IIC 高速モードはわずか 3.4Mbps です。
2. 全二重通信 SPI には 2 本のデータ ラインがあり、1 つのマスターアウト スレーブイン、1 つのマスターイン スレーブアウトがあり、同時にデータの送信とデータの受信が可能ですが、IIC は半二重です。データの送信と受信を同時に行うことはできません。
主なアプリケーションシナリオには、SDカードデータの読み書き、TFTカラースクリーン制御などが含まれます。
この記事では、STM32 ハードウェア SPI インターフェイスを使用して IPS カラー スクリーン ディスプレイ画像を駆動する方法について詳しく説明します (ディスプレイ テキストについては詳しく説明しません。OLED ディスプレイ テキストと同様です)。
SPI 物理インターフェイスの定義を見てみましょう。
MISO – マスター入力スレーブ出力、マスターデバイスデータ入力、スレーブデバイスデータ出力。
MOSI – マスター出力スレーブ入力、マスターデバイスデータ出力、スレーブデバイスデータ入力。
SCLK – マスターデバイスによって生成されるシリアルクロック、クロック信号。
CS – チップ セレクト、スレーブ デバイス イネーブル信号。マスター デバイスによって制御されます。各スレーブ デバイスはマスター デバイスの CS インターフェイスに対応します。スレーブ デバイスと通信したい場合は、CS を介して対応するスレーブ デバイスを有効にします。STM32 では SPI_NSS です。
IIC ソフトウェア シミュレーションに関する記事で、IIC のソフトウェア シミュレーションの実装方法を詳細に分析しましたが、SPI も同様に、GPIO レベルをタイミングに従って制御する限り、ソフトウェア シミュレーションによって実装できます。ここでは、当面はソフトウェア シミュレーションの実装を使用せず、STM32 内でハードウェア SPI を直接設定してから、ライブラリ関数を呼び出します。STM32F103C8T6 を例にとると、このモデルには 2 つの SPI があります。
ここで使用する画面は、解像度 240*240、最大 16 ビット トゥルー カラー (RGB565) サポート、ドライバー チップ ST7789 の 1.3 インチ IPS カラー画面です。
16 ビット True Color は、各ピクセルが 65535 (2^16) 色を持つことを意味します。
三原色の原理によると、ほとんどの色は、赤、緑、青を異なる割合で合成することで生成されます。同様に、ほとんどの単色光も赤、緑、青の 3 色に分解できます。
IPS スクリーンの各ピクセルは、赤、緑、青の遮光シートのセットで構成されており、白色バックライトは遮光シートを通して赤、緑、青の 3 色を表示します。対応する遮光フィルムの角度を変えると色の強度が変化し、異なる強度の赤、緑、青の色が混合されて画面上に表示される 65,535 色が形成されます。
この 16 ビットのうち、最初の 5 ビットは赤の強度を示し、真ん中の 6 ビットは緑の強度を示し、後半の 5 ビットは青の強度を示します。つまり、赤、緑、青はそれぞれ 5、6、5 ビットを占有するため、RGB565 という名前が付けられました。したがって、赤、緑、青の強度はそれぞれ 32、64、32 になります。
ピクセルは 16 ビット (つまり 2 バイト) のデータを占有する必要があり、画面全体では 240x240x2=115200 バイト (つまり 115.2KB) のデータが必要です。
-------ピンの定義:
画面には 7 つのピンがあり、インターフェイス定義とマイコンとの接続方法は次のとおりです。
GND:電源グランド
VCC: 正電源 (3-5V)
SCL: SPI クロックライン。MCU SPI_SCK に接続
SDA: SPI データライン。その画面の場合。データ出力のみでマスターアウト、スレーブインという出力は必要ないのでシングルチップマイコンMOSIに接続します。MCUのMISOは使用しません。
RES:画面をリセットします。MCU が画面と通信する前に、画面を一度リセットする必要があります。つまり、ピンを短時間ローに設定し、その後ハイに設定します (100ms まで使用)。
DC:データ/コマンド選択ポート。以前紹介したOLED画面とは異なり、OLEDは異なるレジスタにデータを書き込むことでコマンドであるかSRAMデータを表示しているかを区別しており、この画面はピンレベルのデータの状態によって現在受信しているデータがコマンドであるか表示であるかを区別します。 。DC ポートが Low の場合はコマンドが書き込まれ、DC ポートが High の場合はデータが書き込まれます。
BLK:画面のバックライト。自発光ピクセルを備えた OLED スクリーンとは異なり、IPS スクリーンは画像を表示するためにバックライトを必要とします。画面のバックライトはデフォルトでオンになっていますが、BLK ポートを Low に設定するとバックライトがオフになります。ここでは、GPIO を使用して制御することも、直接フローティングのままにすることも、ハイレベルに接続することもできます。
STM32での具体的な接続方法は以下の通りです。
TFT 画面の子局は 1 台しかないため、SPI_NSS は接続されません。
------------------------------------------
次に番組を見ながら説明します
1. ヘッダーファイルとGPIOリセット定義
.c ファイルで使用されるペリフェラル ヘッダー ファイル (GPIO、RCC、SPI) を含めます。
PA1、PA2、PA3 の高レベルと低レベルを定義します。51 シングルチップ マイコンとは異なり、51 シングルチップ マイコンでは sbit を使用して変数を GPIO ポートにマッピングできますが、STM32 には sbit がありません。
#include<stm32f10x.h>
#include<stm32f10x_gpio.h>
#include<stm32f10x_rcc.h>
#include<stm32f10x_spi.h>
#define SPI_DC_H() GPIO_SetBits(GPIOA,GPIO_Pin_2) //数据/命令选择
#define SPI_DC_L() GPIO_ResetBits(GPIOA,GPIO_Pin_2)
#define LCD_RES_H() GPIO_SetBits(GPIOA,GPIO_Pin_3) //LCD复位(PA3)
#define LCD_RES_L() GPIO_ResetBits(GPIOA,GPIO_Pin_3)
#define LCD_BLK_H() GPIO_SetBits(GPIOA,GPIO_Pin_1) //LCD背光(PA1)
#define LCD_BLK_L() GPIO_ResetBits(GPIOA,GPIO_Pin_1)
2. 画像を 16 進数データに変換します
有機ELに文字を表示するには、フォントに応じて画面のピクセルを点灯または消灯させることで実現します。同じことが画像の表示にも当てはまります。カラー画面の場合、各ピクセルには 0 または 1 より多くの状態があります。65535 の状態があります。画像を取得したい方法に応じて、各ピクセルを独自の方法にすればよいのです。それを配列に入れることができます。要素は各ピクセルの状態値です。次に、配列の要素を SPI を介して TFT スクリーンの表示バッファ レジスタに順番に送信すると、目的の画像をスクリーンに表示できます。
ここで使用する必要があるガジェットは Img2Lcd です。画像を配列に変換するツールです。このツールはオンラインでダウンロードできます。
次に、次の図を C プログラムの配列に変換する方法を説明します。ここでは240x240の正方形の画面を使用しているので、画面いっぱいに表示するために、画像を意図的に正方形にカットしています。
Img2Lcd は jpg 形式の画像のみ変換できるので、他の形式の画像の場合は、まず jpg 形式に変換する必要があります。
次にガジェットでファイルを開きます
開いたら、関連パラメータを設定する必要があります。出力グレースケールでは 16 ビットの True Color が選択されます。
ここで使用する STM32F103C8T6 には 64KB のフラッシュしか搭載されていないため、配列のサイズが 64KB を超えてはなりません。64KB を超えないと、ストレージ容量不足のためコンパイルに失敗します。ここでは、他の変数やプログラムが占有できるように一定量のフラッシュ マージンを残し、配列に 60KB のスペースを割り当てます。また、画面の 1 ピクセルには 2B のスペースが必要です。つまり、合計 60K ÷ 2 = 30000 のピクセル状態を保存できます。次に、30000 の平方根をとり、約 173 になります。したがって、ここでは最大の幅と高さを 173x173 に設定します。(使用するシングルチップマイコンが128K以上のフラッシュであれば、240×240の設定で全画面表示可能)
「画像ヘッダー データを含める」のチェックを外し、「高ビットを最初に送信する」にチェックを入れます (これは、画面が最初に高ビットを送信し、次に低ビットを送信するためです。これをチェックしないと、色の反転効果が発生します)。最後に、コントラストを少し上げることもできます (IPS スクリーンの色が十分に明るくないため、最終的な画像は少し薄暗くなります)。
設定が完了したら、「最大幅と高さ」の右側にある小さなボタンをクリックすると、以下のようなプレビューが生成されます。
次に、「保存」をクリックし、パスを選択して .c ファイルを取得することを確認します。ここでは、メモ帳で直接開きます。内部に配列があり、要素数は 59859 で、173x173x2 に正確に等しいことがわかります。
ここでの各要素は 2 桁の 16 進数であり、2 要素ごとにピクセルを表します。
次に、その配列をコピーしてプログラムに貼り付けます (下記)。アレイは長いので折りたたむことができます。
以上で本工程は完了する。
3. SPIの初期化
ハードウェアSPI1を使用するため、SPI1の初期化が必要です。
1 つ目は GPIO の設定で、SPI1 は PA4 から PA7 までの合計 4 つの IO を占有し、この 4 つの IO は多重化されたプッシュプル出力として設定されます。さらに、3 つの IO ポート PA1、PA2、PA3 は、それぞれバックライト、データ/コマンド選択、リセット用の制御ポートとして使用されます。したがって、ここでこれら 3 つの IO を初期化し、プッシュプル出力として設定する必要もあります。次に、対応する時計をオンにします。
次に SPI の設定ですが、GPIO と同様に SPI ペリフェラル ライブラリにも構造体があり、その中のメンバが SPI のパラメータになります。構造体の具体的な内容は、周辺ライブラリで確認できます。
以下のコードは参考用です
void SPI_UserInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //PA4为SPI_NSS,PA5为SPI_SCK,PA6为SPI_MISO,PA7为SPI_MOSI,对SPI这4个端口进行初始化
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //PA1为BLK背光,PA2位DC选择,PA3为屏幕复位
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); //使能时钟
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//设置为双向双线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置为SPI主站。控制屏幕,单片机需做主站
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置数据大小为8位,即每次发送8位数据
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //设置串行时钟的稳态为时钟高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //设置位捕捉的时钟活动沿为第一个上升沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //设置NSS为软件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;//设置波特率分频(该值越大,波特率越慢)
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //设置先发送高位
SPI_InitStructure.SPI_CRCPolynomial = 7; //设置CRC校验
SPI_Init(SPI1, &SPI_InitStructure); //初始化
SPI_Cmd(SPI1, ENABLE); //使能SPI1
}
4. SPI書き込みデータと書き込みコマンド
OLED 画面と同様に、データの書き込みとコマンドの書き込みのサブ関数は個別に宣言する必要があります。
前述したように、画面は DC ポートを使用してコマンドとデータを区別するため、データの書き込みとコマンドの書き込みの違いは、DC ポートがそれぞれ High と Low であることです。データの書き込みも 8 ビットデータの書き込みと 16 ビットデータの書き込みに分かれているため、データの書き込み機能も 2 つあります。
SPI 自体は 16 個のデータ フレーム ( SPI_InitStructure.SPI_DataSize で設定) をサポートしますが、8 ビット データ フレームの送信を容易にするために、SPI の初期化で DataSize が 8 に設定されることに注意してください。 16 ビット データの送信は、最初に下位 8 ビットを送信し、次にデータを 8 ビット右にシフトしてから、再度 8 ビット データを送信することによって実現されます。
void SPI_WriteCmd(u8 Data) //写命令
{
SPI_DC_L();
SPI_I2S_SendData(SPI1,Data);
}
void LCD_WriteData8(u8 Data) //写8位数据
{
SPI_DC_H();
SPI_I2S_SendData(SPI1,Data);
}
void LCD_WriteData16(u16 Data) //写16位数据
{
SPI_DC_H();
SPI_I2S_SendData(SPI1,(Data>>8) ) ; //Date右移8位
SPI_I2S_SendData(SPI1,Data);
}
5. 画面の初期化
OLED スクリーンと同様に、IPS の電源を入れた後、初期化のためにパラメータを設定する必要があります。
設定する必要がある特定のパラメータについては、ST7789 マニュアルを参照してください。以下のパラメータを参照できます。
void LCD_Init(void)
{
u16 x;
LCD_RES_L();
Delay_ms(50);
LCD_RES_H();
Delay_ms(50); //复位屏幕
SPI_WriteCmd(0x3A); //设置颜色格式为16位真彩
LCD_WriteData8(0x05); //03 4K;05 65K;06 262K
SPI_WriteCmd(0x36); //设置屏幕方向为从上到下,从左到右
LCD_WriteData8(0x00);
//----------- ST7789S Frame rate setting ---------//
SPI_WriteCmd(0xB2);
LCD_WriteData8(0x0C);
LCD_WriteData8(0x0C);
LCD_WriteData8(0x00);
LCD_WriteData8(0x33);
LCD_WriteData8(0x33);
SPI_WriteCmd(0xB7);
LCD_WriteData8(0x35);
//----------- ST7789S Power setting ---------//
SPI_WriteCmd(0xBB);
LCD_WriteData8(0x19);
SPI_WriteCmd(0xC0);
LCD_WriteData8(0x2C);
SPI_WriteCmd(0xC2);
LCD_WriteData8(0x01);
SPI_WriteCmd(0xC3);
LCD_WriteData8(0x12);
SPI_WriteCmd(0xC4);
LCD_WriteData8(0x20);
SPI_WriteCmd(0xC6);
LCD_WriteData8(0x0F);
SPI_WriteCmd(0xD0);
LCD_WriteData8(0xA4);
LCD_WriteData8(0xA1);
//----------- Posistive Voltage Gamma Control ---------//
SPI_WriteCmd(0xE0);
LCD_WriteData8(0xD0);
LCD_WriteData8(0x04);
LCD_WriteData8(0x0D);
LCD_WriteData8(0x11);
LCD_WriteData8(0x13);
LCD_WriteData8(0x2B);
LCD_WriteData8(0x3F);
LCD_WriteData8(0x54);
LCD_WriteData8(0x4C);
LCD_WriteData8(0x18);
LCD_WriteData8(0x0D);
LCD_WriteData8(0x0B);
LCD_WriteData8(0x1F);
LCD_WriteData8(0x23);
SPI_WriteCmd(0xE1);
LCD_WriteData8(0xD0);
LCD_WriteData8(0x04);
LCD_WriteData8(0x0C);
LCD_WriteData8(0x11);
LCD_WriteData8(0x13);
LCD_WriteData8(0x2C);
LCD_WriteData8(0x3F);
LCD_WriteData8(0x44);
LCD_WriteData8(0x51);
LCD_WriteData8(0x2F);
LCD_WriteData8(0x1F);
LCD_WriteData8(0x1F);
LCD_WriteData8(0x20);
LCD_WriteData8(0x23);
//----------- Setting ---------//
SPI_WriteCmd(0x21);
SPI_WriteCmd(0x11)
SPI_WriteCmd(0x29);
LCD_SetRegion(0,0,239,239);
SPI_DC_H();
for(x=0;x<57600;x++) //清屏
{
LCD_WriteData16(0xffff);
}
LCD_BLK_H();
}
関数の後に、画面をクリアするために使用されるループをここに追加しました。電源を切ると画面内に表示されているSRAMのデータが消えてしまうためです。電源を入れるたびに、最後に電源を切る前の画像は表示されず、次のような雪の結晶画面が表示されます。
画面をクリアする原理は、ディスプレイ SRAM にデータを再度書き込み、すべてのピクセルを特定の状態 (色として認識できる) で埋めることです。ここでは 0xffff を入力します。0xffff は RGB565 の純白なので、最終的な効果は白い画面になります。0x0000 を入力すると黒で塗りつぶされ、最終的な効果は黒い画面になります。もちろん、必要に応じて任意の色を塗りつぶすこともできます。
このサイクルは一度に 1 ピクセルを埋めていき、全画面には合計 57600 ピクセルがあるため、サイクルは 57600 回になります。
6. 表示領域を設定する
表示領域は長方形の領域なので、始点と終点を設定する必要があります。
0x2a: セット(x0,x1)
0x2b: セット(y0, y1)
void LCD_SetRegion(u16 x0, u16 y0, u16 x1, u16 y1)//设置显示区域(x0,y0,x1,y1)
{
SPI_WriteCmd(0x2a);
LCD_WriteData16(x0);
LCD_WriteData16(x1);
SPI_WriteCmd(0x2b);
LCD_WriteData16(y0);
LCD_WriteData16(y1);
SPI_WriteCmd(0x2c); //下面发送的是数据
}
7. SPI転送アレイ
STM32 の SPI 送信データのライブラリ関数は SPI_I2S_SendData(SPIx,Deta) です。
画像配列には 59859 個の要素があるため、59859 個のデータを送信する必要があります。データを送信する前に、DC ポートを High に設定する必要があります。
void image(void)
{
u16 x;
SPI_DC_H(); //DC置高,发送数据
for(x=0;x<59860;x++) //循环填充像素
{
SPI_I2S_SendData(SPI1,gImage_girl[x]); //将图片数组的元素通过SPI发送给IPS屏
}
}
8. 主な機能
GPIO、SPI、ISP 画面の初期化、表示領域の座標の設定、データの送信を行います。
画像を画面の中央に表示できるようにするには、表示領域を中央に設定する必要があります。
ここで簡単な計算をすると、240-173=67, 67/2≈33 なので、座標 (x0, y0) は (33, 33) に設定でき、画像の解像度は 173 x173 なので、x1 の座標は
、 y1 は (33+173-1, 33+173-1)、つまり 205,205 である必要があります。ここで1を引く必要がある理由は、小学校の算数の「植樹問題」と似ています。したがって、領域を制限する関数は (33,33,205,205) となります。
int main(void)
{
SPI_UserInit();
LCD_Init();
LCD_SetRegion(33,33,205,205);
image();
}
最終的な表示効果は次のとおりです。
115KBを超えるFlashを搭載したシングルチップマイコンであれば全画面表示可能です(カメラ撮影のため、画像が有彩色に見えます)。
Flashが小さい場合は複数回に分割して全画面表示することも可能です。
以上の手順により、画面上に様々な写真や動画を表示することができます。
「悪いリンゴ」のデモ
表示テキスト
この記事では、SPI 通信を使用して IPS 画面に画像を表示する方法を説明し、デモンストレーションします。どのようなシングルチップマイコンであっても、搭載されているFlashは非常に限られており、実際に使用する際には内蔵Flashに画像を直接保存することはできません。最も一般的に使用される方法は、画像を SD カードに保存し、マイクロコントローラーが SD カードからデータを読み取ることです。SDカードの使い方については記事後半で解説します。
アニメーション効果の作成やビデオの再生など、その他の高度な使用方法については、後ほど紹介します。