STM32のレジスタを叩く。 #3 ~SPI編~
お久しぶりです。約半年ぶりのまともな(?)更新です。
先日、部品箱を漁っていたらこんなものを見つけました。
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
BR
まずは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)か、を表します。
モード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でコマンドを送信することで、ディスプレイを初期化することが出来ました。
おわりに
SPIは設定項目が多く、リファレンスマニュアルを読むのに少し苦労しました。 リファレンスマニュアルを読む上でDeepL(https://www.deepl.com/ja/translator)が非常に役に立ったので、英語の資料等読む機会がある方にはオススメです。
CPU創ってみた
タイトルでお察しかと思いますが、名著「CPUの創りかた」の4bitCPUである「TD4」を実際に創ってみたという記事になります。
n番煎じどころではないのですが、一応記録としてブログにも残しておこうと思います。(ツイート貼っただけですが)
今更ながらCPU創るよ
— rpy (@rpyjp) 2019年11月4日
ユニバーサル基板で組み立てる根性はないので、技術書典にて @dip_factory 様のプリント基板を購入させて頂きました🙇 #TD4 pic.twitter.com/UVOspMrwoW
まずはクロック&リセット回路
— rpy (@rpyjp) 2019年11月4日
丁度1Hzくらい出てていい感じ pic.twitter.com/nd6SrbwW4o
続いてプログラムカウンタ、と思ったら早速問題発生
— rpy (@rpyjp) 2019年11月4日
手動クロックだと上手くいくのに自動クロックでは正しくカウントされない
原因が分からん... pic.twitter.com/CmpBNWU8IQ
R12の値を大きくすることで一時的に解決した
— rpy (@rpyjp) 2019年11月4日
E12系列だと470Ω以上にすればちゃんと動くっぽい(?) pic.twitter.com/E4u1MLAOWf
気を取り直してデコーダとROM横のLEDを実装
— rpy (@rpyjp) 2019年11月4日
カメラが小刻みに震えてるのは作るのに夢中で昼飯食べ忘れたから pic.twitter.com/YtKEySoXVY
2step分のROMとインバータ付けた!
— rpy (@rpyjp) 2019年11月4日
ちゃんとROMの内容読めてるぽい pic.twitter.com/ngYrpwaLpF
命令デコーダを実装した
— rpy (@rpyjp) 2019年11月5日
一通り命令を入力して正しくデコード出来ていることを確認 pic.twitter.com/eHvKls2BfD
レジスタA, Bを取り付けてIN A, IN B命令の動作を確認
— rpy (@rpyjp) 2019年11月5日
LEDも増えてきてそれっぽくなってきた(?) pic.twitter.com/E16X35UK1H
ALUが付いてJNC以外の全ての命令を実行出来るようになった
— rpy (@rpyjp) 2019年11月6日
カラフルなLEDとDIPのICにロマンを感じる pic.twitter.com/7SdxQSlHnX
出力となるLEDも付けたので残すは最後の砦であるROMの実装のみ
— rpy (@rpyjp) 2019年11月6日
長引きそうなので今日はここまで pic.twitter.com/5sARTWrWd6
自作CPUついに完成!🎉
— rpy (@rpyjp) 2019年11月7日
最後は計128個のダイオードと16個のdipスイッチをひたすら取り付ける作業でなかなか苦労した
動画はさっそくラーメンタイマーのプログラムを実行してる様子
人生で一番美味しいカップ麺になるだろう#TD4 pic.twitter.com/tQLX45OoeX
STM32のレジスタを叩く。 #2 ~UART編~
今回はUARTを使ってみます。
STM32F446は、4つのUSARTと2つのUARTを持っています。このうちUSART2は、NucleoのUSB-シリアル変換機能によって、基板上のUSBポートからアクセスすることが出来ます。
まずはGPIOと同様、ペリフェラルにクロックを供給することから始めます。
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
ボーレートの設定
続いてボーレートの設定を行います。 ユーザマニュアルによると、ボーレートは次のような式によって決められます。
この式を使って、ボーレートが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
この値を実際に設定するのは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として使えるよう設定を行います。
データシートの表を見ると、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
GPIOx_AFRは、それぞれのポートの各ピンごとに、どのオルタネート機能を使うかを設定するレジスタです。このレジスタはGPIOx_AFRLとGPIOx_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]); } }
正しく受信することが出来ました。
STM32のレジスタを叩く。 #1 ~GPIO編~
今回は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
このレジスタではピンのモード(入出力方向など)を設定します。
Reset valuesを見ると、一部のピンを除いて"00 (Input)"で初期化されています。 LD2はPA5に接続されているので、GPIOAのMODER5を"01 (General purpose output mode)"に設定します。 具体的には次のように記述します。
GPIOA->MODER |= (1 << 10);
これでもよいのですが、1や10といった数字が何を表すのか分かりづらく、可読性が高いとは言い難いです。 stm32f446xx.hには、こういったレジスタの設定に便利な定義がたくさん用意されています。 同じくstm32f4xx_hal_gpio.hにも便利な定義があるのでこれも使っちゃいましょう。 すると、
GPIOA->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODER5_Pos);
このように記述することが出来ます。先ほどの例と比べて、この行で何が行われているのかが分かりやすくなったかと思います。
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を使うにはこれを有効にする必要があります。
データシートのブロック図を見ると、GPIOAはAHB1に接続されていることがわかります。
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が点滅しました。
STM32のレジスタを叩く。 #0 ~準備編~
お久しぶりです、気づけば前回の投稿から約半年が経ちました。アウトプットって続かないですね。
この半年間を振り返ると、応用情報技術者試験にも無事合格し、ネットワークスペシャリストを受験したり、初めてハッカソンに出場してモノづくりをしたり、また技術書典やMakerFaire等の技術系のイベントにも行ってみるなど、新しいことをたくさん始められた半年だったなあと感じます。
さて、今回はまた新たなことを始めようと、STM32の開発/評価ボードのひとつであるNUCLEO-F446REを購入してみました。
あれ、LPCは?
STM32 Nucleo Board STM32F446RE: マイコン関連 秋月電子通商-電子部品・ネット通販
STM32といえば、HALやLL(またはSPL)といったAPI(またはライブラリ)を利用してプログラムを書くのが一般的(?)かと思います。ネットでSTM32に関する記事を探すと、APIを利用したものがほとんどです。 これらのAPIを利用することで、開発者はレジスタやクロック等、ハードウェアの細かいところを意識することなく開発をすることができます。
しかし、今回はあえてAPIを利用せずに、直接レジスタを叩いて開発してみようと思います。 「なぜわざわざそんなことをするのか、素直にAPI使えばいいのに。」という話なんですが、その通りです。
APIを挟むとオーバーヘッドが動作に影響を及ぼすとか、そんな真っ当な(?)理由ではなく、なんとな〜くAPIによってブラックボックス化されている部分の中身が気になるとか、もともとAVRマイコンを触っていたこともあり、レジスタを叩くほうが慣れてるとか、まあそんな感じの理由です。そもそもSTM32のリソースをフルに使える程のスキルなんてないですし。
ということで、このシリーズはSTM32の各種ペリフェラルをレジスタから利用する方法を、自分用ノートとしてまとめておく感じにしようと思います。 相変わらずマイペースな更新になるかと思いますがよろしくお願いします。
データシートとリファレンスマニュアル
さて、レジスタを直接叩くとなるとデーターシートやリファレンスマニュアルを読むことは必須となります。
まずは以下のリンクからデータシートとリファレンスマニュアルをブックマークまたはダウンロードしておきます。
データシート
リファレンスマニュアル
開発環境
次に開発環境を整えます。このシリーズでは以下の環境で開発することを前提とします。
OS
Ubuntu 18.04LTS
STM32 開発/評価ボード
Nucleo F446RE (STM32F446)
初期化コード生成ツール
CubeMX www.st.com
Toolchain
GNU ARM Embedded Toolchain developer.arm.com
書き込みツール
st-link github.com
新規プロジェクト作成手順
開発環境が整ったら、まずは新規プロジェクトを作成します。
CubeMXを起動し、File から New Project... を選択します。 ウィンドウが出てくるので、ここで開発に使用するボードまたはMCUを選びます。
Project ManagerのProjectタブでProject Nameを設定します。 また、Toolchain / IDEでMakefileを選択します。
Code Generatorタブに移動し、画像のように設定します。
GENERATE CODEを押すと、空のプロジェクトが生成されます。
例:
####################################### # binaries ####################################### BINPATH = PATH/gcc-arm-none-eabi/bin
これで新規プロジェクトの作成と設定は完了です。$ make all
でmakeできることを確認します。
書き込み
NucleoをPCに接続し、
$ cd build $ st-flash write <project_name>.bin 0x8000000
で書き込みができます。 コマンドが正常に終了すればOKです。
Arch Linuxをインストールしたので備忘録
※一部記事の内容が古くなっていることがあります。実際にインストールを行う際は、Arch Wikiの最新のインストールガイドに従ってインストールすることを推奨します。
Arch Linuxをインストールしたので備忘録として手順を残しておきます。
※この記事はあくまで私の環境でのインストール手順の記録になります。環境が異なるとインストール手順も異なることがあるため、実際にインストールする際はArch Wikiのインストールガイドを参照しながら進めることをお勧めします。また、当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。
目次
- 目次
- インストールメディアのダウンロード
- インストールメディアをUSBメモリに書き込む
- ライブ環境の起動
- キーボードレイアウトの変更
- 起動モードの確認
- パーティショニング
- パーティションのフォーマット
- パーティションのマウント
- インターネットに接続する
- システムクロックの更新
- ミラーの選択
- ベースシステムのインストール
- fstabの生成
- ルートディレクトリの変更
- タイムゾーンの設定
- ロケールの設定
- ホスト名の設定
- ネットワークの設定
- 初期RAMディスクの生成
- rootパスワードの設定
- ブートローダのインストール
- 再起動
- おわりに
インストールメディアのダウンロード
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
また、md5sum
やsha1sum
コマンドで、ダウンロードしたインストールメディアのハッシュとダウンロードページにある最新リリースのチェックサムとを比較することができます。
インストールメディアをUSBメモリに書き込む
sudo fdisk -l
などで、インストールメディアを書き込むUSBメモリのデバイス名を確認しておきます。
dd
コマンドでUSBメモリに書き込みます。
$ sudo dd if=./archlinux-<version>-x86_64.iso of=</dev/sdx> bs=1048576
※デバイス名を間違えないように注意!
ライブ環境の起動
インストールメディアを書き込んだUSBメモリを挿入し、コンピュータを立ち上げます。USBブートができるよう、予めBIOS(UEFI)の設定でブートの優先順位を変更しておきます。
ライブ環境が立ち上がるとメニューが表示されるので、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>
# 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
インストールメディアを取り除き、新しいシステムにログインします。
無事 Arch Linuxを起動することができました。
おわりに
Linux初心者の私にとって、Arch Linuxはインストールから難易度の高いディストリビューションというイメージがありました。しかし、親切なArch Wikiのおかげで、ひとまずインストール作業を終えることができました。 また、Arch Linuxをインストールするにあたって、Linuxの起動に必要なパーティションやブートローダなど、普段Linuxを利用しているだけではなかなか触れない部分についても知ることができ、非常に勉強になりました。
今後はデスクトップ環境の構築などについても記事にしていけたらと思います。
aitendoの超小型OLEDを使ってみる #2
前回の記事では制御ICの初期設定と画面の表示を行いました。 が、この状態では画像データRAM(GDDRAM)の初期化を行っていないため、ノイズが表示されてしまいます。
GDDRAMの構成
ノイズをクリアするためには、GDDRAMをすべて0で初期化する必要があります。
データシートを参照すると、SSD1306のGDDRAMは大きく8つのページに分かれていることが分かります。
それぞれのページは、画面上側をLSB、画面下側をMSBとする8bitのセグメントが横に128列並んでいる構造をしています。
つまり0x00
を128回送信し、それを8回繰り返すことで画面をすべてクリアすることができます。
アドレッシングモード
画面の描画方法ですが、SSD1306には3種類のモード(アドレッシングモード)が用意されています。 その中でも今回は、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回送信すれば、画面のクリアは完了です。