STM32F303K8 & mbedでSoftwareSerialを実装する 【TX編】

# UART、好きですよね?
こんにちは。Yuseiと申します。普段はダラダラ好きなコードを書いて遊んだり、雑に高校生をやったり、ロボットを作ったりしています。

今回は私自身ロボットでよく使っている、シリアル通信の方式 "UART" で少し遊んでみました。

なお、タイトル通りのことの結果だけ知りたい人は、[TXを実装する](#txを実装する)まで飛んでください。

#UARTについて
## そもそも論 -- UARTとは
>UART (Universal Asynchronous Receiver/Transmitter, ユーアート) は、調歩同期方式によるシリアル信号をパラレル信号に変換したり、その逆方向の変換を行うための集積回路である。
https://ja.wikipedia.org/wiki/UART

ということなんだそうです。　先ほど私はUARTを「シリアル通信の方式」と書きましたが、これは正しくもあり(そういうコンテクストで使われるので)、間違いでもある(元はTransmitterやReceiverの**ハードウェアの名前**だから)ということになります。
「通信ペリフェラルの一種」という言い方をすれば無難でしょうか.


調歩同期方式(=**非同期通信**)です。それゆえに、以下の特徴があります。

- 受信側と送信側の**両方がクロックを持ち**、**同じクロックで**動作することで、通信を実現しています。
- 信号線一本(&GND)で片道の通信ができます。もう一本つけると往復で通信できます。
- マスター、スレーブの区別がなく、対等に通信します。

以下に、接続の概略を示します。　TX(Transmit)とRX(Receive)の配線がクロスするのが特徴です。
<img width="500" alt="IMG_B6CFDD40EDE4-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/bff1d689-dfcd-a895-894a-ce86e18c1d7c.jpeg">

これは、TX、RXそれぞれのピン名がそれぞれのマイコン自身から見た名前であり、RX受信するには相手のTXから出ている信号が欲しい、TX送信するにはRXに流し込みたいという風になるからです。

それでは、この中の信号はどうなっているのでしょうか。

### UARTの信号とは
このセクションに関しては、
- [UART通信のシリアル波形をアナライザで解析してみた](https://misoji-engineer.com/archives/uart.html)
- [USARTの使い方](http://www.picfun.com/f1/f04.html)

 を合わせて読んでいただければ完全理解できるはずですが、一応ここにも書いておきます。


以下が、基本的なUART通信の信号です。 デジタル信号ですので、TTLレベル/CMOSレベルのいずれかに揃えて,HIGH/LOWの２値でやり取りします。

<img width="685" alt="スクリーンショット 2019-08-31 21.19.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200465/de0d0066-784f-1d42-7e95-2226938a348f.png">

1. 通信はまず、信号ラインがHIGHの状態からLOWになるところ(立ち下がり)で始まります
2. そこから**1bit分の時間**はLowに保ちます。この `0`になるビットが**スタートビット**です
3. ここからデータの送信を始めます。送信の単位は1バイトです。1バイトに含まれる8つの各ビットの0/1 に合わせて、、**ビッグエンディアン**(=1の位から上位の位への順)で、電圧をLOW/HIGHしていきます。各ビットのHIGH/LOWを保つ時間の長さは**1bit分の時間**です。
4. (パリティビットを使うときは、DB7がパリティになります。)
5. 最後に**1bit分の時間**かそれ以上長くHIGHに保ちます。これが**ストップビット**です。


### 1bit分の時間

先に述べたように、UARTは非同期通信方式です。
そのため、それぞれが同じ周期のクロックを持って通信を行い、それぞれが**1bit分の時間**を計ります。
その**通信全体で共通してもつクロック**の周期を、**baudrate**(ぼーれーと)と呼びます。
baudrateが速い方がより高速で通信できます。baudrateは、そのまま通信の速度です。単位はbpsで、一般に`9600`,`115200`あたりがよく用いられるようです。

このbaudrateから、簡単に**1bit分の時間**が計算できます。

というのも、**bps**は **bit per second**です。　
つまり**1秒あたり何ビット送るか**ということになります。
これをくるっとひっくり返して、**1ビットあたりに何秒かけるか**を求めれば良いわけですから、bpsの逆数をとれば良いのです。

**(1bit分の時間)=   1   / (baudrate)** です。

#### 時間の誤差について
上で求めた時間ですが、実はある程度の誤差を許容します。
言い換えれば、規定の時間よりも**1bit分の時間**が多少長かったり,短かったりしても、普通に通信ができます。これは、10bit送り切って1bit分以上ののずれが出ない程度まで許容されるということになります。



## ソフトウェアシリアル

説明が長くなりました。
今回やってみた「遊び」は、UARTを、普通のマイコンのGPIOでやってみるというやつです。
普通、マイコンからUARTを用いる際には、専用のペリフェラルチップがSoCに含まれていて、それに接続された特定のピンを用いて行います。

そのため、利用できるUARTペリフェラルの数はある程度限られてきます。
しかし、前のセクションまででご紹介したように、実はUARTの信号はかなり単純です。
そこで、UARTの信号を、ソフトウェアからGPIOのHIGH/LOWに変化させることで生成し、ソフトウェア的にUARTを実装しようということを試みました。

ちなみに、Arduinoには、同様のことを試みた`SoftwareSerial`というライブラリがあります。今回は私のUARTの勉強を兼ねていたのでそちらのコードは参照していないのですが、Arduino界隈ではかなり頻繁に使われるライブラリです。

なお、今回やってみるマイコンとして選んだのは STmicro Instruments社のSTM32F303k8t6で、mbedフレームワークで動かしました。


## TXを実装する
とりあえず1byteポーリングで, `'A'`を送信するコードを下記に示します。


~~~c
#include <mbed.h>
#define UART_DELAY 104 //  1/9600
Serial pc(USBTX,USBRX);
DigitalOut RX(D4);
DigitalOut TX(D5);


int main() {
TX=1;
pc.baud(230400);
pc.printf("Boot");

  while(1) {

    TX=0;//START
    wait_us(UART_DELAY);

    TX=1;//DB0
    wait_us(UART_DELAY);

    TX=0;//DB1
    wait_us(UART_DELAY);

    TX=0;//DB2
    wait_us(UART_DELAY);

    TX=0;//DB3
    wait_us(UART_DELAY);

     TX=0;//DB4
    wait_us(UART_DELAY);

     TX=0;//DB5
    wait_us(UART_DELAY);

     TX=1;//DB6
    wait_us(UART_DELAY);

     TX=0;//DB7
    wait_us(UART_DELAY);

     TX=1;//Stop
    wait(1);

  }
}
~~~

これだけです。　これだけで、`'A'`を送信することができる**はず**です。
確認には、FTDI社のFT232RLを用いたUSB-シリアル変換アダプタを用いました。
以下のように接続してください。

| STM32 | FTDI |
|---|---|
|D4|TX|
|D5|RX|
|GND|GND|

なお、このマイコンは3.3vで動作しているので、そのようにアダプタを設定してさい。

このプログラムでの送信は、**9600 8-N-1** (9600bps、8bit幅,パリティなし、ストップビット1)で設定しています。


### ハマりポイント: 時間について

このプログラムでは、**1bit分の時間**を 定義値 `UART_DELAY` で定義しています。単位はμsです。この値は、本来 1/baudrate (s)になるはずで、baudrateが9600bpsのときは1/9600≒104μs となるはずなのですが、私が確認したところでは**これではうまく通信できませんでした。** 
代わりに、**91<UART_DELAY<102**の範囲で設定することで、動作が確認できました。

#### 時間が短くなる理由

ある程度マイコンを触った方ならお分かりかもしれませんが、**GPIOの設定には時間がかかります**。

実際に、以下のコードで(正確ではありませんが)簡単にそれにかかる時間を測定してみました。

~~~c
#include <mbed.h>
#define UART_DELAY 97//101Max,92 Min(104 in theory)
Serial pc(USBTX,USBRX);
DigitalOut RX(D4);
DigitalOut TX(D5);

Timer t;

int main() {

  // put your setup code here, to run once:
    TX=1;
    pc.baud(230400);
    pc.printf("Boot");

    while(1) {

        TX=1;//START
        t.reset();
        t.start();
        TX=0;//START
        t.stop();
        pc.printf("Time:%d useconds.\n",t.read_us());
        wait_ms(300);
  }
}
~~~

シリアルモニターには以下のように出てきました。

~~~
Time:8 useconds.
Time:7 useconds.
Time:7 useconds.
Time:8 useconds.
Time:7 useconds.
Time:8 useconds.
      :
      :
~~~

これから、ピンの変化には**およそ**7,5μ秒かかっていることがわかります。

これを用いると、先ほどの変化の理由が見えてきます。
つまり、元の104μ秒-7.5μ秒=96.5μ秒となり、`UART_DELAY`の変域の中心である96.5μ秒に**ぴったり一致**します。

以上より、実際にこのmbedソフトウェアシリアルを用いる際には、7.5μs(実際には8μ秒くらいがいいと思います)ずらして扱うのが良いでしょう。あるいは、ピンを変化させてから,`Ticker`を用いるのもアリかもしれませんね。

私はTXは一旦これで実験レベルには満足したので、また気が向いたらリファクタリングをしてクラスに整理して、`putc` `printf`や`RxIrq`を実装しようと思います。
おそらく来週中には終わると思いますので、続報をお楽しみにしていただければ幸いです。そのうちGPLv3あたりのライセンスでライブラリとして公開すると思います。

# To Be continued ... RX編に続く!
FIFOバッファ作るの、楽しいね。