エビデイエビナイ電子工作

やったことの記録

STM32のレジスタを叩く。 #3 ~SPI編~

f:id:Rappy:20191113182922j:plain

お久しぶりです。約半年ぶりのまともな(?)更新です。


先日、部品箱を漁っていたらこんなものを見つけました。


1.3 インチ 240*240 IPS スクリーン ST7789 ドライブ IC SPI 通信 3.3 ボルト電圧 SPI インタフェースフルカラー液晶 OLED ディスプレイ


1.3インチと小型ながら、240*240の解像度を持つ、フルカラーのグラフィックディスプレイです。 ドライバICには、ST7789が採用されています。

このディスプレイはSPIで通信を行うので、今回はこれで遊びながら、STM32でSPIを扱うのに必要なレジスタの設定をまとめていこうと思います。


SPI

SPI(Serial Peripheral Interface)は、モトローラ(現 NXPセミコンダクターズ)が提唱したシリアルインターフェースです。 基本的には、SCK, MISO, MOSIの3線に、SSを加えた4種類の信号線で通信を行います。

SPIは設定項目が多く、使用するスレーブデバイスに合わせていくつかの項目を設定する必要があります。

  • 通信速度
  • SPIモード
  • 通信方式(全二重/半二重/単方向)
  • データ幅
  • 先頭ビット

など


今回はこれらの項目を、ディスプレイに合わせて次のように設定することにします。

設定項目 設定値
通信速度 1Mbps
SPIモード モード2
通信方式 単方向(送信のみ)
データ幅 8bit
先頭ビット MSB


レジスタの設定

リファレンスマニュアルの 26.3.7 SPI configuration に従って、レジスタの設定行います。

SPI_CR1

f:id:Rappy:20200521221135p:plain
SPI_CR1

BR

まずはBRビットで、通信速度の設定を行います。

f:id:Rappy:20200521223443p:plain
BR
1Mbpsで通信を行うため、内蔵16MHzを16分周してSPIのクロックとします。

SPI1->CR1 |=  SPI_CR1_BR_0 | SPI_CR1_BR_1;        // fpclk/16


CPOL, CPHA

続いてSPIモードの設定を行います。 SPIでは、クロックの極性(CPOL)とクロックの位相(CPHA)によって、4つのモードが定義されています。

SPIモード CPOL CPHA
モード0 0 0
モード1 0 1
モード2 1 0
モード3 1 1

クロックの極性(CPOL)とは、通信を行っていない間、SCKがHIGH(CPOL=1)か、LOW(CPOL=0)か、を表します。

また、クロックの位相(CPHA)は、SCKの最初のエッジでサンプリングを行う(CPHA=0)か、二番目のエッジでサンプリングを行う(CPHA=1)か、を表します。

f:id:Rappy:20200522204336p:plain
CPOLとCPHA

モード2で通信を行うため、CPOL=1, CPHA=0 に設定します。

SPI1->CR1 |=  SPI_CR1_CPOL;        // CPOL = 1
SPI1->CR1 &= ~SPI_CR1_CPHA;        // CPHA = 0


RXONLY, BIDIMODE, BIDIOE

通信方式の設定には、RXONLY, BIDIMODE, BIDIOE の3つのビットを使用します。

送信のみの単方向通信は、全二重通信と同様の設定を行い、受信関係のビットを無視することで実現します。 従って、これらのビットはデフォルト(全二重通信)のままとします。


LSBFIRST

LSBFIRSTビットで、通信における先頭ビットを定義します。 LSBFIRST=0 とすることで、MSBを先頭ビットにします。

SPI1->CR1 &= ~SPI_CR1_LSBFIRST;        // MSB first


SSM, SSI

SSM, SSIビットでSSピンの管理を行います。

通常、SPIマスターは、SS(Slave Select)信号を使って通信するスレーブを選択します。 しかし、今回使用するディスプレイにはSSピンがありません。

SSピンを使用しないときは、SSM=1とすることで、SSピンを他の用途に使用することが出来ます。 また、SSI=1 とすることで、MODFエラーを防ぎます。

SPI1->CR1 |=  SPI_CR1_SSM;        // SSM = 1
SPI1->CR1 |=  SPI_CR1_SSI;        // SSI = 1


MSTR

STM32をSPIマスターとして使用するため、MSTRビットを設定します。

SPI1->CR1 |=  SPI_CR1_MSTR;        // Master configuration


DFF

DFFビットでデータ幅を選択します。 DFF=0 で8bit、DFF=1 で16bitです。

SPI1->CR1 &= ~SPI_CR1_DFF;        // 8-bit data frame format


SPE

以上の設定が終わったら、SPEビットを立ててSPIを有効にします。

SPI1->CR1 |= SPI_CR1_SPE;        // SPI Enable


SPI_CR2

SSOE

今回は使用しませんが、SSピンを使う場合、SPI_CR2のSSOEビットを有効にする必要があります。


データの送信

ディスプレイに対してコマンドや画像データを送るために必要な、データの送信方法を示します。

これは、他のペリフェラルと同様、ステータスレジスタを監視してデータレジスタにデータを書き込むことで実現できます。 今回も簡単のため、ビジーループによってステータスレジスタを監視します。

void spi_send(uint8_t data){
    while(!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = data;
}


送信のみの単方向通信を行う際の注意点

送信のみの単方向通信は、実際には全二重通信の受信側を無視しているだけです。 そのため、意図せずRXNEフラグやOVRフラグが立つことがありますが、これらは無視してもよいです。(よく見るとリファレンスマニュアルに書いてあるが、筆者はちゃんと読んでなかったせいで時間を溶かした。)


サンプルコード

以上を踏まえ、初期化とデータ送信の例を提示します。 事前にペリフェラル自体へのクロック供給を忘れずに行います。

/* SPI 初期化 */
void spi_init(void){
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    spi_io_init();
    SPI1->CR1 |=  SPI_CR1_BR_0 | SPI_CR1_BR_1;        // fpclk/16
    SPI1->CR1 |=  SPI_CR1_CPOL;        // CPOL = 1
    SPI1->CR1 &= ~SPI_CR1_CPHA;        // CPHA = 0
    SPI1->CR1 &= ~SPI_CR1_LSBFIRST;        // MSB first
    SPI1->CR1 |=  SPI_CR1_SSM;        // SSM = 1
    SPI1->CR1 |=  SPI_CR1_SSI;        // SSI = 1
    SPI1->CR1 |=  SPI_CR1_MSTR;        // Master configuration
    SPI1->CR1 &= ~SPI_CR1_DFF;        // 8-bit data frame format
    SPI1->CR1 |= SPI_CR1_SPE;        // SPI Enable
}

/* SPI 初期化(IO)*/
void spi_io_init(void){
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

    /* PB3 SCK */
    GPIOB->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER3_Pos);
    GPIOB->AFR[0] |= (GPIO_AF5_SPI1 << GPIO_AFRL_AFSEL3_Pos);

    /* PB5 MOSI */
    GPIOB->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER5_Pos);
    GPIOB->AFR[0] |= (GPIO_AF5_SPI1 << GPIO_AFRL_AFSEL5_Pos);
}

/* SPI 送信 */
void spi_send(uint8_t data){
    while(!(SPI1->SR & SPI_SR_TXE));
    SPI1->DR = data;
}


グラフィックディスプレイを動かす

ディスプレイの初期化シーケンスに従ってSPIでコマンドを送信することで、ディスプレイを初期化することが出来ました。

f:id:Rappy:20200522001134j:plain
ディスプレイを初期化することができた


おわりに

SPIは設定項目が多く、リファレンスマニュアルを読むのに少し苦労しました。 リファレンスマニュアルを読む上でDeepL(https://www.deepl.com/ja/translator)が非常に役に立ったので、英語の資料等読む機会がある方にはオススメです。


twitter.com

CPU創ってみた

タイトルでお察しかと思いますが、名著CPUの創りかたの4bitCPUである「TD4」を実際に創ってみたという記事になります。

n番煎じどころではないのですが、一応記録としてブログにも残しておこうと思います。(ツイート貼っただけですが)

CPUの創りかた - 渡波 郁 単行本(ソフトカバー)




twitter.com

STM32のレジスタを叩く。 #2 ~UART編~

f:id:Rappy:20191114111502j:plain 今回はUARTを使ってみます。

STM32F446は、4つのUSARTと2つのUARTを持っています。このうちUSART2は、NucleoのUSB-シリアル変換機能によって、基板上のUSBポートからアクセスすることが出来ます。

まずはGPIOと同様、ペリフェラルにクロックを供給することから始めます。

RCC->APB1ENR |= RCC_APB1ENR_USART2EN;


ボーレートの設定

続いてボーレートの設定を行います。 ユーザマニュアルによると、ボーレートは次のような式によって決められます。 f:id:Rappy:20191205183232p:plain

この式を使って、ボーレートが115200となるようなUSARTDIVの値を求めます。

ここで、fckはクロックの周波数です。まだPLLやプリスケーラの設定をしていないので内蔵16MHzが使われます。 また、OVER8はオーバーサンプリングというもので、ここはデフォルト値(0)のまま使うこととします。

これらの値を代入すると、

$$ USARTDIV = \frac{16 \times 10^{6} }{8 \times (2 - 0) \times 115200} = 8.6805... $$

USARTDIVの値が、8.6805... に求まりました。

USART_BRR

f:id:Rappy:20191205225835p:plain
USART_BRR
この値を実際に設定するのはUSART_BRRレジスタです。実部をDIV_Mantissaに、小数部をDIV_Fractionに入れます。 つまりこのレジスタには、USARTDIVを4bit左シフトした値を代入すればよいということになります。

...ところで、先ほどの式は次のように表すこともできます。

$$ USARTDIV \times 16 = \frac{16 \times 10^{6}}{115200} $$

USARTDIVに16を掛けることは、USARTDIVを4bit左シフトすることと同義なので、OVER8が0の場合は、単にfckをボーレートで割った値を代入すればよいということになります。(ただし整数同士で除算する関係で、1bit分の誤差が生じる可能性がある。)

USART2->BRR |= 16000000 / baudrate;


ピンの設定

次に、ピンをUSART2のTX/RXとして使えるよう設定を行います。

f:id:Rappy:20191207023209p:plain
Alternate function
データシートの表を見ると、USART2のTX/RXはそれぞれ PA2, PA3で使用できることがわかります。

ピンをGPIO以外のペリフェラルで使いたいときは、該当するピンをGPIOx_MODERレジスタ"10 (Alternate function mode)"に設定します。

GPIOA->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER2_Pos);
GPIOA->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER3_Pos);

GPIOx_AFR

f:id:Rappy:20191207153217p:plain
GPIOx_AFRL
GPIOx_AFRは、それぞれのポートの各ピンごとに、どのオルタネート機能を使うかを設定するレジスタです。このレジスタGPIOx_AFRLGPIOx_AFRHの二つに分かれています。

PA2, PA3をUSART2(AF7)で使うよう設定します。

GPIOA->AFR[0] |= (GPIO_AF7_USART2 << GPIO_AFRL_AFSEL2_Pos);
GPIOA->AFR[0] |= (GPIO_AF7_USART2 << GPIO_AFRL_AFSEL3_Pos);

最後に USART_CR1レジスタでTX/RX、そしてUSART2を有効にすれば、初期化は完了です。

  USART2->CR1 |= USART_CR1_TE | USART_CR1_RE;
  USART2->CR1 |= USART_CR1_UE;


文字の送受信

1文字を送信/受信する関数を、それぞれ簡単に実装してみます。 基本的にはどちらも、USART_SRレジスタでフラグが立つのを待ち、USART_DRレジスタでデータの受け渡しを行うといった流れになります。 今回は簡単のため、whileで待ちをつくります。

送信

void usart_send_char(char c){
  while(!(USART2->SR & USART_SR_TXE_Msk));
  USART2->DR = c;
  while(!(USART2->SR & USART_SR_TC_Msk));
}

受信

char usart_get_char(void){
  while(!(USART2->SR & USART_SR_RXNE_Msk));
  char buf = USART2->DR;
  return buf;
}


Hello, World! してみる

STM32から一定間隔で "Hello, World!" を送信するプログラムを実行してみます。

#include "stm32f4xx.h"

void usart_init(uint32_t baudrate);
void usart_send_char(char c);
void usart_send_str(const char*);

int main(void){
  usart_init(115200);

  while(1){
    for(volatile uint32_t i=0;i<1000000;i++);
    usart_send_str("Hello, World!\r\n");
  }
  return 0;
}

void usart_init(uint32_t baudrate){
  RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

  USART2->BRR |= 16000000 / baudrate;

  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

  /* USART2 TX (PA2) */
  GPIOA->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER2_Pos);
  GPIOA->AFR[0] |= (GPIO_AF7_USART2 << GPIO_AFRL_AFSEL2_Pos);
  GPIOA->PUPDR |= (GPIO_PULLUP << GPIO_PUPDR_PUPD2_Pos);

  /* USART2 RX (PA3) */
  GPIOA->MODER |= (GPIO_MODE_AF_PP << GPIO_MODER_MODER3_Pos);
  GPIOA->AFR[0] |= (GPIO_AF7_USART2 << GPIO_AFRL_AFSEL3_Pos);
  GPIOA->PUPDR |= (GPIO_PULLUP << GPIO_PUPDR_PUPD3_Pos);

  USART2->CR1 |= USART_CR1_TE | USART_CR1_RE;
  USART2->CR1 |= USART_CR1_UE;
}

void usart_send_char(char c){
  while(!(USART2->SR & USART_SR_TXE_Msk));
  USART2->DR = c;
  while(!(USART2->SR & USART_SR_TC_Msk));
}

void usart_send_str(const char *str){
  for(uint8_t p=0;str[p]!='\0';p++){
    usart_send_char(str[p]);
  }
}


f:id:Rappy:20191207234118p:plain 正しく受信することが出来ました。


twitter.com

STM32のレジスタを叩く。 #1 ~GPIO編~

f:id:Rappy:20191114111502j:plain

今回はGPIO編です。早速Lチカからやってみましょう。

エディタでSrcディレクトリにあるmain.cを開くと、CubeMXが生成した初期化コードが記述されています。

この初期化コードには、CubeMXで設定したピンの機能やクロックの設定等が反映されています。CubeMXを利用することで、これらの設定をGUIで行うことができ、開発をスピーディーに進められます。


...が、このシリーズではクロックの設定も含めてレジスタから行うのでmain.cの中身を全て消します。(涙)

代わりにstm32f4xx.hをインクルードしておきましょう。

#include "stm32f4xx.h"

また、今回Lチカに使うLEDは、NUCLEO-F446REにはじめから実装されているもの(LD2)とします。

GPIOの設定

RM0390(リファレンスマニュアル)から設定が必要なレジスタを探します。

GPIOx_MODER

f:id:Rappy:20191116150051p:plain
GPIOx_MODER
このレジスタではピンのモード(入出力方向など)を設定します。

Reset valuesを見ると、一部のピンを除いて"00 (Input)"で初期化されています。 LD2はPA5に接続されているので、GPIOAのMODER5を"01 (General purpose output mode)"に設定します。 具体的には次のように記述します。

GPIOA->MODER |= (1 << 10);

これでもよいのですが、1や10といった数字が何を表すのか分かりづらく、可読性が高いとは言い難いです。 stm32f446xx.hには、こういったレジスタの設定に便利な定義がたくさん用意されています。

f:id:Rappy:20191116150735p:plain
stm32f446xx.h
同じくstm32f4xx_hal_gpio.hにも便利な定義があるのでこれも使っちゃいましょう。
f:id:Rappy:20191116180159p:plain
stm32f4xx_hal_gpio.h
すると、

GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODER5_Pos);

このように記述することが出来ます。先ほどの例と比べて、この行で何が行われているのかが分かりやすくなったかと思います。

GPIOx_ODR

f:id:Rappy:20191116150053p:plain
GPIOx_ODR
GPIOx_ODRは各ポートが出力する値を設定するレジスタです。 LEDを点滅させるにはPA5の出力を一定時間ごとにトグルさせれば良いので、適当にforで待ちを作った後、GPIOAのODR5ビットを反転させます。

while(1){
    for(volatile uint32_t i=0;i<1000000;i++);
    GPIOA->ODR ^= GPIO_ODR_OD5;
}

これで完了です!


...と言いたいところですが、STM32では省電力のため、ペリフェラルへのクロック供給がデフォルトで無効になっています。GPIOを使うにはこれを有効にする必要があります。

f:id:Rappy:20191117221105p:plain
STM32F446xC/E のブロック図

データシートのブロック図を見ると、GPIOAはAHB1に接続されていることがわかります。

RCC_AHB1ENR

f:id:Rappy:20191117203325p:plain
RCC_AHB1ENR
AHB1に接続されているペリフェラルへのクロック供給を有効/無効化するためのレジスタです。 GPIOAを使うには、このレジスタGPIOAENビットを立てる必要があります。

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

今度こそ完了です。 以上のことを踏まえてコードを示します。

#include "stm32f4xx.h"

int main(void){
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    GPIOA->MODER &= ~GPIO_MODER_MODER5_Msk;
    GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODER5_Pos);

    while(1){
        for(volatile uint32_t i=0;i<1000000;i++);
        GPIOA->ODR ^= GPIO_ODR_OD5;
    }
    return 0;
}

前回同様 makeして書き込むと、LEDが点滅しました。

www.youtube.com


twitter.com

STM32のレジスタを叩く。 #0 ~準備編~

f:id:Rappy:20191113182922j:plain

お久しぶりです、気づけば前回の投稿から約半年が経ちました。アウトプットって続かないですね。

この半年間を振り返ると、応用情報技術者試験にも無事合格し、ネットワークスペシャリストを受験したり、初めてハッカソンに出場してモノづくりをしたり、また技術書典やMakerFaire等の技術系のイベントにも行ってみるなど、新しいことをたくさん始められた半年だったなあと感じます。

さて、今回はまた新たなことを始めようと、STM32の開発/評価ボードのひとつであるNUCLEO-F446REを購入してみました。 あれ、LPCは? STM32 Nucleo Board STM32F446RE: マイコン関連 秋月電子通商-電子部品・ネット通販

STM32といえば、HALやLL(またはSPL)といったAPI(またはライブラリ)を利用してプログラムを書くのが一般的(?)かと思います。ネットでSTM32に関する記事を探すと、APIを利用したものがほとんどです。 これらのAPIを利用することで、開発者はレジスタやクロック等、ハードウェアの細かいところを意識することなく開発をすることができます。

しかし、今回はあえてAPIを利用せずに、直接レジスタを叩いて開発してみようと思います。 「なぜわざわざそんなことをするのか、素直にAPI使えばいいのに。」という話なんですが、その通りです。

APIを挟むとオーバーヘッドが動作に影響を及ぼすとか、そんな真っ当な(?)理由ではなく、なんとな〜くAPIによってブラックボックス化されている部分の中身が気になるとか、もともとAVRマイコンを触っていたこともあり、レジスタを叩くほうが慣れてるとか、まあそんな感じの理由です。そもそもSTM32のリソースをフルに使える程のスキルなんてないですし。

ということで、このシリーズはSTM32の各種ペリフェラルレジスタから利用する方法を、自分用ノートとしてまとめておく感じにしようと思います。 相変わらずマイペースな更新になるかと思いますがよろしくお願いします。

データシートとリファレンスマニュアル

さて、レジスタを直接叩くとなるとデーターシートやリファレンスマニュアルを読むことは必須となります。

まずは以下のリンクからデータシートとリファレンスマニュアルをブックマークまたはダウンロードしておきます。

データシート

Datasheet - STM32F446xC/E

リファレンスマニュアル

RM0390

開発環境

次に開発環境を整えます。このシリーズでは以下の環境で開発することを前提とします。

OS

Ubuntu 18.04LTS

STM32 開発/評価ボード

Nucleo F446RE (STM32F446)

akizukidenshi.com

初期化コード生成ツール

CubeMX www.st.com

Toolchain

GNU ARM Embedded Toolchain developer.arm.com

書き込みツール

st-link github.com

新規プロジェクト作成手順

開発環境が整ったら、まずは新規プロジェクトを作成します。

  1. CubeMXを起動し、File から New Project... を選択します。 ウィンドウが出てくるので、ここで開発に使用するボードまたはMCUを選びます。

    f:id:Rappy:20191113191136p:plain
    開発に使用するボードまたはMCUを選択

  2. Project ManagerのProjectタブでProject Nameを設定します。 また、Toolchain / IDEMakefileを選択します。

    f:id:Rappy:20191113191716p:plain
    Projectの設定

  3. Code Generatorタブに移動し、画像のように設定します。

    f:id:Rappy:20191113194750p:plain
    Code Generatorの設定

  4. GENERATE CODEを押すと、空のプロジェクトが生成されます。

    f:id:Rappy:20191113201347p:plain
    空のプロジェクトが生成される

  5. 生成されたMakefileのbinariesにgcc-arm-none-eabiのPATHを追記します。

例:

#######################################
# binaries
#######################################
BINPATH = PATH/gcc-arm-none-eabi/bin

これで新規プロジェクトの作成と設定は完了です。$ make allでmakeできることを確認します。

書き込み

NucleoをPCに接続し、

$ cd build
$ st-flash write <project_name>.bin 0x8000000

で書き込みができます。 コマンドが正常に終了すればOKです。


twitter.com

Arch Linuxをインストールしたので備忘録

f:id:Rappy:20190325103015p:plain

※一部記事の内容が古くなっていることがあります。実際にインストールを行う際は、Arch Wikiの最新のインストールガイドに従ってインストールすることを推奨します。

Arch Linuxをインストールしたので備忘録として手順を残しておきます。

※この記事はあくまで私の環境でのインストール手順の記録になります。環境が異なるとインストール手順も異なることがあるため、実際にインストールする際はArch Wikiインストールガイドを参照しながら進めることをお勧めします。また、当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。

目次


インストールメディアのダウンロード

archlinux.jpのダウンロードページから最新版のインストールメディア(archlinux-<version>-x86_64.iso)と、必要に応じてPGP署名(archlinux-<version>-x86_64.iso.sig)をダウンロードします。

GnuPGがインストールされている場合、以下のコマンドを実行すれば検証できます。

$ gpg --keyserver pgp.mit.edu --keyserver-options auto-key-retrieve --verify archlinux-<version>-x86_64.iso.sig

また、md5sumsha1sumコマンドで、ダウンロードしたインストールメディアのハッシュとダウンロードページにある最新リリースのチェックサムとを比較することができます。


インストールメディアをUSBメモリに書き込む

sudo fdisk -lなどで、インストールメディアを書き込むUSBメモリのデバイス名を確認しておきます。

ddコマンドでUSBメモリに書き込みます。

$ sudo dd if=./archlinux-<version>-x86_64.iso of=</dev/sdx> bs=1048576

※デバイス名を間違えないように注意!


ライブ環境の起動

インストールメディアを書き込んだUSBメモリを挿入し、コンピュータを立ち上げます。USBブートができるよう、予めBIOSUEFI)の設定でブートの優先順位を変更しておきます。

ライブ環境が立ち上がるとメニューが表示されるので、Arch Linux archiso x86_64 UEFI CDを選択します。(UEFI環境の場合)

最初に仮想端末に root ユーザーでログインされ、Zsh のシェルプロンプトが表示されます。


キーボードレイアウトの変更

デフォルトのキーボードレイアウトはUSキーボードなので、日本語キーボードレイアウトに変更しておきます。

# loadkeys jp106


起動モードの確認

efivars をチェックして起動モードを確認します。

# ls /sys/firmware/efi/efivars

ディレクトリが存在していればUEFIモードで起動しています。

※以降 UEFI環境でのインストール手順になります。


パーティショニング

Arch Linuxをインストールするデバイスには、ルートディレクトリのパーティション(以下 ルートパーティション)と、EFIシステムパーティション(以下 ESP)が必要です。

事前にインストールするデバイスのデバイス名とパーティションテーブルの形式を確認しておきます。

# fdisk -l

パーティションテーブルの形式がGPTだったのでgdiskを使用します。

# gdisk </dev/sdx>

gdiskのプロンプトが立ち上がったらoで新しいパーティションテーブルを作成し、既存のパーティションデータを全て消去します。

※データが含まれているディスクに新しいパーティションテーブルを作成すると、ディスク上の全てのデータが消去されます。


nで新しいパーティションを作成します。 パーティションを作成するには次の4つの項目を設定します。


まずはESPを作成してみます。 ESP のタイプidはef00で、容量は 512 MiB にすることが推奨されているので、4つの項目を次のように設定します。

項目
パーティションの番号 デフォルト
開始セクタ デフォルト
終末セクタ +512M
パーティションのタイプid ef00


つぎにルートパーティションを作成します。 Linux filesystem のタイプidは8300です。今回は残りの容量すべてをルートパーティションに割当てることにします。

項目
パーティションの番号 デフォルト
開始セクタ デフォルト
終末セクタ デフォルト
パーティションのタイプid 8300


パーティションの作成が完了したらwで書き込んでgdiskを終了します。


パーティションのフォーマット

パーティションを作成したら、適切なファイルシステムでフォーマットする必要があります。

ESPはFAT32でフォーマットします。

# mkfs.fat -F32 </dev/sdxY>

ルートパーティションext4でフォーマットしておきます。

# mkfs.ext4 </dev/sdxZ>


パーティションのマウント

ルートパーティション/mntに、ESPを/mnt/bootにマウントします。

# mount </dev/sdxZ> /mnt
# mkdir /mnt/boot
# mount </dev/sdxY> /mnt/boot


インターネットに接続する

今回は有線で接続することにします。 有線接続の場合、dhcpcd が起動時に有効化され自動的に接続を開始します。

pingを使って接続が確立されているかどうか確かめます。

# ping archlinux.jp

接続できない場合は、dhcpcd の状態を確認します。

# systemctl status dhcpcd@<interface>

<interface>はTab補完で入力できます。

私の場合、コマンドの実行結果がActive: inactive(dead)となっていたので、systemd でdhcpcd を起動・有効化しました。

# systemctl start dhcpcd
# systemctl enable dhcpcd


システムクロックの更新

systemd-timesyncd を使ってシステムクロックを正確にします。

# timedatectl set-ntp true


ここまででインストールの準備は完了です。


ミラーの選択

/etc/pacman.d/mirrorlistを編集してミラーを選択します。位置的に最も近いミラーを選択するのがベストです。

特定のミラーを有効にするには使いたいミラーをアンコメントします。

例:

# Japan
# Server = ftp://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch
# Japan
Server = ftp://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch


ベースシステムのインストール

pacstrapを使って base グループと base-devel パッケージグループをインストールします。

# pacstrap /mnt base base-devel


ここからはシステムの設定を行います。


fstabの生成

genfstabコマンドで fstab を生成します。

# genfstab -U /mnt >> /mnt/etc/fstab


ルートディレクトリの変更

ルートディレクトリを新しくインストールしたシステムに変更します。

# arch-chroot /mnt


タイムゾーンの設定

タイムゾーンの設定を行います。 ここではタイムゾーンを東京に設定します。

# ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

hwclockを実行して /etc/adjtime を生成します。

# hwclock --systohc --utc


ロケールの設定

/etc/locale.genを編集して使用するロケールをアンコメントします。

例:

~
#en_US.UTF-8 UTF-8
~
#ja_JP.UTF-8 UTF-8
~
~
en_US.UTF-8 UTF-8
~
ja_JP.UTF-8 UTF-8
~

コマンドを実行します。

# locale-gen

ロケールen_US.UTF-8に設定します。

# echo LANG=en_US.UTF-8 > /etc/locale.conf

必要に応じてvconsole.confでコンソールキーマップとフォントを設定します。

# echo KEYMAP=jp106 > /etc/vconsole.conf


ホスト名の設定

<hostname>に任意のホスト名を設定します。

# echo <hostname> > /etc/hostname

同じ名前を/etc/hostsにも記述します。

127.0.0.1    localhost
::1     localhost
127.0.1.1   <hostname>.localdomain    <hostname>


ネットワークの設定

新しくインストールされた環境ではネットワーク接続がデフォルトで有効になっていないので、もう一度ネットワークの設定を行ないます。


初期RAMディスクの生成

必要に応じて/etc/mkinitcpio.conf を設定し、初期RAMディスクを生成します。

# mkinitcpio -p linux


rootパスワードの設定

rootパスワードを設定します。

# passwd


ブートローダのインストール

Arch Linux を起動するためにブートローダを選択してインストールする必要があります。 今回はGRUBをインストールすることにします。

bashシェルにログインしていることを確認し、grub と efibootmgr パッケージをインストールします。

# pacman -S grub efibootmgr

ブートディレクトリとブートローダIDを設定してインストールします。

# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=arch-grub

インストールした後は、メインの設定ファイルgrub.cfgを生成する必要があります。

# grub-mkconfig -o /boot/grub/grub.cfg

IntelまたはAMDのCPUを使っている場合、マイクロコードのアップデートを有効にします。

私はIntelのプロセッサを使用しているので、intel-ucode をインストールし、設定ファイルを再生成します。

# pacman -s intel-ucode
# grub-mkconfig -o /boot/grub/grub.cfg


再起動

chroot 環境から抜けます。

# exit

rebootでマシンを再起動します。

# reboot

インストールメディアを取り除き、新しいシステムにログインします。


f:id:Rappy:20190325153736j:plain

無事 Arch Linuxを起動することができました。



おわりに

Linux初心者の私にとって、Arch Linuxはインストールから難易度の高いディストリビューションというイメージがありました。しかし、親切なArch Wikiのおかげで、ひとまずインストール作業を終えることができました。 また、Arch Linuxをインストールするにあたって、Linuxの起動に必要なパーティションブートローダなど、普段Linuxを利用しているだけではなかなか触れない部分についても知ることができ、非常に勉強になりました。

今後はデスクトップ環境の構築などについても記事にしていけたらと思います。


twitter.com

aitendoの超小型OLEDを使ってみる #2

f:id:Rappy:20190215165246j:plain

前回の記事では制御ICの初期設定と画面の表示を行いました。 が、この状態では画像データRAM(GDDRAM)の初期化を行っていないため、ノイズが表示されてしまいます。


GDDRAMの構成

ノイズをクリアするためには、GDDRAMをすべて0で初期化する必要があります。


f:id:Rappy:20190302130905p:plain
GDDRAMの構成

データシートを参照すると、SSD1306のGDDRAMは大きく8つのページに分かれていることが分かります。


f:id:Rappy:20190302131513p:plain
ページの構成

それぞれのページは、画面上側をLSB、画面下側をMSBとする8bitのセグメントが横に128列並んでいる構造をしています。

つまり0x00を128回送信し、それを8回繰り返すことで画面をすべてクリアすることができます。


アドレッシングモード

画面の描画方法ですが、SSD1306には3種類のモード(アドレッシングモード)が用意されています。 その中でも今回は、Horizontal addressing modeというモードを使ってみます。


f:id:Rappy:20190302134847p:plain
Horizontal addressing mode

Horizontal addressing mode は、描画を開始するセグメントから右方向にポインタを進めていき、終了する列(カラム)に到達したら次のページに進む。という動きを繰り返しながら描画していきます。

今回は画面全体を描画範囲とするので、座標をそれぞれ次のように設定します。

  • 開始カラム:0
  • 終了カラム:127
  • 開始ページ:0
  • 終了ページ:7

ちなみに、他のアドレッシングモードでは、ポインタの進む方向や座標指定の方法などが変わってきます。興味のある方はデータシートを確認してみて下さい。


画面をクリアしてみる

以上のことを踏まえて、実際に画面をクリアしてみます。

具体的には、前回のプログラムに次の項目を追加します。

  • アドレッシングモード指定:0x20
  • Horizontal addressing mode:0x00
  • カラム指定:0x21
  • 開始カラム(0):0x00
  • 終了カラム(127):0x7F
  • ページ指定:0x22
  • 開始ページ(0):0x00
  • 終了ページ(7):0x07

ここで一度通信を終了し、画像データを送るためにD/C#ビットを1にして再び通信を開始します。

あとは0x00を128*8回送信すれば、画面のクリアは完了です。


f:id:Rappy:20190302164540j:plain
ノイズが表示されなくなった


参考


twitter.com