PC上で動作するプログラムのデバッグ出力は簡単にできます。C言語ならprintf()
、javascriptならconsole.log()
を使えば、出力ウィンドウやコンソール上にログ出力されます。
PIC開発の場合も、MPLABX上でC言語を使えばprintf()
で同様の出力ができますが、ちょっと一癖あります。その手順の備忘録を残しておきます。
UARTでシリアル通信を使って標準出力を行う
ほとんどのPICには、EUSART(Enhanched Universal Synchronous Asynchronous Receiver Transmitter ここではUARTと呼ぶ)というハードウェアが内蔵されています。UART送信できるようにしておけば、TXREGレジスタに書き込んだ文字がTXピンから送信されて、相手のRXピンを経由して伝達します。これがシリアル通信の一種です。
実機の場合、PCにUSBシリアル変換機を接続して、そのRXピンとPICのTXピンをワイヤ接続します。USBシリアル変換機のポートに対してTeraTermなどのコンソールアプリを起動すると、転送された文字が表示されるようになるわけです。
Simulatorを使ったコンソール出力は、このUARTを利用して送信した文字をSimulatorが受信して、文字列をウィンドウに表示させる仕組みになっている。プログラムの実装は、上記の実機のシリアル通信で必要なものと同じで、出力先だけを変えるようなイメージである。
printf()が使えるようにする準備
デフォルトの状態だと、プログラムでprintf("hello world\n")
と記述しても何も出力されないです。まずprintf
関数を実行すると、上記のUARTの送信用バッファのレジスタTXREG
に書き込むようなプログラムを実装する必要があります。
コンパイラによって、実装が違うので注意。XC8の例は、Microchipのデベロッパー向けサイトにある。
Printing to the UART Console in MPLAB® X IDE Simulator
XC8 (8bit)コンパイラの場合
putch()
という名称で、以下のように関数を実装する。内部処理はTXIF
ビットがONになったら、TXREG
に1文字書き込む内容になっている。この関数は、printf()
内部で何度もコールされます。
#include <stdio.h>
void putch(unsigned char data){
while(PIR1bits.TXIF != 1){
continue;
}
TXREG = data;
}
UARTを有効にするために以下の2つのビットをONにする必要があります。
TXSTAbits.TXEN = 1;
RCSTAbits.SPEN = 1;
XC8を使うPICの場合はこれでOK。
XC16 (16bit)コンパイラの場合
XC16の場合のputch()
相当のものが以下になります。これを実装します。注意点としては以下のサンプルでは、U1STA
やU1TXREG
レジスタを使っています。PIC24FJ64GB002の場合、UARTが2機あるようなので、どちらを使うか(U1STAかU2STA)を決めておきます。
int __attribute__((__section__(".libc.write"))) write(
int handle, void *buffer, unsigned int len) {
int i;
while(U1STAbits.TRMT == 0);
for (i = len; i; --i){
while(U1STAbits.TRMT == 0);
U1TXREG = *(char*)buffer++;
}
return(len);
}
UARTを有効にするビットをONします。こちらもU1とU2使うレジスタを合わせておきます。
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
MCCを使う場合
MCC(Microchip Code Configuratior)を使ってUARTを追加するだけで、上記のコードは自動で追加されます。ただし、下図のオプションをONしてからGenerateする必要があります。
Generateすれば、すでに利用可能になっています。
サンプルプログラム
XC8コンパイラ(PIC16F1455を使用)のサンプル
// XC8, PIC16F1455 Sample Code
:
#include <stdio.h>
:
void putch(unsigned char data){
while(PIR1bits.TXIF != 1){
continue;
}
TXREG = data;
}
void main(void)
{
TXSTAbits.TXEN = 1;
RCSTAbits.SPEN = 1;
printf("Hello World!\n");
while(1){
// Add your application code
}
return;
}
XC16コンパイラ(PIC24FJ64GB002を使用)のサンプル
// XC16, PIC24FJ64GB002 Sample Code
:
#include <stdio.h>
:
int __attribute__((__section__(".libc.write"))) write(
int handle, void *buffer, unsigned int len) {
int i;
while(U1STAbits.TRMT == 0);
for (i = len; i; --i)
{
while(U1STAbits.TRMT == 0);
U1TXREG = *(char*)buffer++;
}
return(len);
}
int main(void)
{
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
printf("Hello World!\n");
while(1){
// Add your application code
}
return -1;
}
コンソール出力するためのシミュレータの準備
XC16のプロジェクト使った手順になりますが、XC8も同じです。まずは、プロジェクト設定で、Hardware ToolとしてSimulatorを選択します。
続いて、Simulatorの設定を選択して、プルダウンからUart1 IO Optionsを選択して切り替えます。Enable Uart1 IOをONを選択します。次にOutputとしてWindowを選択します。(XC8の場合など使用するPICによって名称は変わりそうですが、UARTの出力云々だと思います)
デバッグ実行を行います。
デバッグ実行されると、MPLABXの下段のOutputウィンドウに、UARTの出力タブが自動的に追加されて、printf()
の内容がコンソールに出力されるようになる。
1点気になることとしては、XC8の場合、最後の1文字がTXREGに残ったまま転送されないです。(Flushされてないようなイメージで、上記の場合だと最後の改行文字\n
がコンソールには出力されてないです。次の出力時に押し出されるように出力されます。)自動Flushのようなオプションがあれば出力されそうです。
PC上のソフトウェア開発と違って、お約束のHelloWorldのコンソール出力が結構メンドクサイですが、デバッガの使い勝手がちょっと悪いので、上記のようにコンソール出力できるようになっておくと便利です。今回はシミュレータで出力していますが、実機の場合でもprintf
を使う場合は同様の実装が必要になります。PIC開発では必須の小技だと思います。
シミュレータも使いこなせるようになると、便利そうですが使い勝手にやはり一癖あるので、ちょっとずつ覚えていく予定。