PSoCのUARTを使ってみる
by K.I
2005/08/12〜
Index
- ワンチップマイコンをCOMポートで繋いで、何かやりたい。
- プログラムのデバッグをするのに、COMポートのデータを表示するものが欲しい。
- もう1台、PCを繋いでも良いんだけど。
- 昔なら、とりあえずMODEMとか繋いで試したりしたけど、今は気軽に繋げられるものがないなぁ。
- というわけで、COMポートに繋げられる表示器を作ろう。
- とりあえずワンチップマイコンには、CypressのPSoCを使うことにする。
[top]
- UARTの使い方は、まずサンプルコードを見てみよう。
#include <m8c.h>
#include "PSoCAPI.h"
void main()
{
char * strPtr; // Parameter pointer
UART_CmdReset(); // Initialize receiver/cmd
// buffer
UART_IntCntl(UART_ENABLE_RX_INT); // Enable RX interrupts
Counter8_WritePeriod(155); // Set up baud rate generator
Counter8_WriteCompareValue(77);
Counter8_Start(); // Turn on baud rate generator
UART_Start(UART_PARITY_NONE); // Enable UART
M8C_EnableGInt ; // Turn on interrupts
UART_CPutString("\r\nWelcome to PSoC UART test program. V1.1 \r\n");
while(1) {
if(UART_bCmdCheck()) { // Wait for command
if(strPtr = UART_szGetParam()) { // More than delimiter?
UART_CPutString("Found valid command\r\nCommand =>");
UART_PutString(strPtr); // Print out command
UART_CPutString("<\r\nParamaters:\r\n");
while(strPtr = UART_szGetParam()) { // loop on each parameter
UART_CPutString(" <");
UART_PutString(strPtr); // Print each parameter
UART_CPutString(">\r\n");
}
}
UART_CmdReset(); // Reset command buffer
}
}
}
サンプルプログラムを、眺めてみたが。。
- うーん、なんだこれ。わかんない。。。
- 最初、割込み許可してるけど、割込みルーチンが無い。。
- あとは、クロック作ってるだけで、いきなりプログラムが始まってる。
- コマンド受信を待って、コマンドとパラメータをコールバック表示するように見えるなぁ。
- 割込みルーチンは、Library Source の uart_1_int.asmに入ってた。
- つまり、コマンドとパラメータ程度は受け付けるライブラリになってるのかぁ。
とりあえず、HyperTerminalを繋いでみる。
- しかし、実際動かしてみると最初の表示は出るが、コマンド受信がコールバックされない。
じゃ、動かない理由は何だ?
- 送信出来ない?
- 受信出来ない?
- RS232Cドライバの出力(Rx側)をオシロで確認→OKに見える
- 割込みルーチンは、多分ライブラリに入ってるやつなんだろうし
- 割込みは許可してるしなぁ
- ライブラリの使い方が間違ってるのかな?
- 接続をチェックしよう
- 繋がってるのは、RxがP1_3、TxがP1_2で、OKだ。
- ポート設定は、、Strongだ。。。→あれ、High-Zにならないなぁ
配線をチェックしてみる。
- Tx,Rxともに、GlobalOutputバス経由で繋いでる。
- あぁ、これか。。そういえばずーっと入力設定してなかったからなぁ。うっかり間違えてた。
- GlobalInputバス経由に直したら、、、OKでした。
- これは普通のCPUだと、たぶん間違えないだろうなぁ。
- PSoCはDesignerで設定するから、見た目繋がってるから安心しちゃうのかも。。
- PCと繋ぐ場合、コマンドとパラメータを指定して動かすっていう用途は多そうだから、実用的なAPIかもしれない。
[top]
表示器は、以下のような構成にする。
- 表示器は、16(20)文字×2行のLCDを使う
- タクトSW×2個
- LED×2個
といっても、これは「はじめてのPSoC」の回路と同じ構成だ。
とりあえず、欲張らずに作ってみよう。
- 1行目は、受信データのコマンドだけ表示する。
- 2行目は、バーグラフを表示する。
- 2個のタクトスイッチで増減させて、タクトスイッチを押した時の値を送信する。
こんな感じでどうかな。
- メインプログラムは、UARTのコマンド処理として
- タクトSWの入力は割込みで処理することにしよう。
コマンドが来た時だけ、送信するようにした方が良いかもしれないけど。。ちゃんと考えるのも面倒なので、これで良いや。。
- 受信は既にライブラリにUART割込みルーチンがあるので作る必要がない。
- これに、送信用のタクトSWによる割込みを追加する1。
- SW状態が変化した時に、データ送信する
- ロータリーエンコーダでも動作するように、もう一方のスイッチがOFFであることを確認するようにした。
#pragma interrupt_handler PSoC_GPIO_ISR()
#define SW_UP 0x02
#define SW_DOWN 0x40
#define MAX_CNT 49
unsigned char count=25;
unsigned char psw=0xff;
// ---- show barGraph
void set_BG()
{
char line[16];
LCD_1_Position(1,0);
lcd_print_d(line,2,count); // printf("%2d",count); (LCD数値表示マクロ)
LCD_1_DrawBG(1,2,14,count);
}
// ---- send count value
void send_count()
{
UART_1_PutSHexInt(count);
UART_1_CPutString(" ");
set_BG();
}
// ---- read switch
void PSoC_GPIO_ISR()
{
unsigned char csw;
csw = PRT0DR; // 現在のSW状態を読む
if ((csw & SW_DOWN) && (csw & SW_UP)) { // SWが全てOFF状態
if ((~psw & SW_DOWN) && (psw & SW_UP)) { // 直前がSW_DOWNなら
count = (count-1+ MAX_CNT) % MAX_CNT; // count down!
send_count();
}
if ((~psw & SW_UP) && (psw & SW_DOWN)) { // 直前がSW_UPなら
count = (count+1) % MAX_CNT; // count up!
send_count();
}
}
psw = csw; // save past SW
}
- UARTの設定と割込み許可
- LCD設定
- GPIO設定と割込み許可
- メインループはコマンド処理のみとする。
// ---- Main program
void main()
{
char *strPtr; // Parameter pointer
// ---- UART setup
UART_1_CmdReset(); // Initialize receiver/cmd buf.
UART_1_IntCntl(UART_1_ENABLE_RX_INT); // Enable RX interrupts
UART_1_Start(UART_1_PARITY_NONE); // Enable UART
UART_1_CPutString("\r\nWelcome to PSoC UART program\r\n");
// ---- LCD setup
LCD_1_Start(); // Initialize LCD hardware
LCD_1_Position(0,0); // Position cursor
LCD_1_PrCString(">PsoC UART Prog.");
LCD_1_InitBG(LCD_1_SOLID_BG); // Initialize BarGraph
set_BG();
// ---- GPIO setup
PRT0DR = SW_UP | SW_DOWN; // Pull-up
INT_MSK0 |= INT_MSK0_GPIO; // enable GPIO interrupt
M8C_EnableGInt; // Turn on interrupts
// ---- UART loop
while(1) {
if(UART_1_bCmdCheck()) { // Wait for command
if(strPtr = UART_1_szGetParam()) { // More than delimiter?
UART_1_PutString(strPtr); // Print out command
LCD_1_Position(0,1); // Position cursor
LCD_1_PrCString(" ");
LCD_1_Position(0,1); // Position cursor
LCD_1_PrString(strPtr); // display command
UART_1_CPutString(" ( ");
while(strPtr=UART_1_szGetParam()) {// loop on each parameter
UART_1_PutString(strPtr); // Print each parameter
UART_1_CPutString(" ");
}
UART_1_CPutString(")");
}
UART_1_PutCRLF();
UART_1_CmdReset(); // Reset command buffer
}
}
}
- チャタ処理のようなことは特にやってないけど、LCD表示が丁度良いディレイになってるようで、小気味良くバーグラフが動く。
- 以前に 試した時にロータリーエンコーダはタイマ割込みで動かすと良いと思ったけど、簡単な用途ならスイッチによる割込みも悪くない。場合によりけりってことか。
案外、すんなり動いたなー。
1C言語のルーチンは、頭にアンダーバーを付けて、ljmp _PSoC_GPIO_ISRとして呼び出す必要がある事に注意。
[top]
PCでコントロールするんだから、PC側のプログラムも必要だ。
- 最近、LabViewを使う機会があり、ちょっとしたコントロールプログラムを作る場合に良いように感じた。。
- 出来るかどうか分からないが、試しにLabViewでプログラムを作ってみよう。
- もちろん、LabViewは持っていないので、評価版のLabViewを使う。
- LabViewでシリアルポートを使うモジュールを探してみると、見当たらないなぁ。
- 良く探すと、VISAモジュールというのがある。
- これは、シリアルポートだけじゃなく、測定器のインターフェースを抽象化して使うライブラリのようだ。
- とにかく、テストプログラムを書いてみる。
- 良く分かんないけど、Open→Write→Read→Closeって感じかな。
- それだけだと、何のインターフェースか分からないので、最初にシリアルポート構成というのを付けてみる。
- 動かすと、エラー -1073807202 が発生した。
- やっぱり評価版じゃダメなのかな?
- VISAライブラリがインストールされていないのかもしれない。
- 早速、ダウンロードしてインストールしてみる。
- 今度は -107380734のエラーが出る。。。
- 位置情報が不十分か、デバイスまたはリソースがシステムに存在しません。ってVISAのインストールがうまく行ってないのかなぁ。
- NIのサイトから、VISAのチェック方法を見て、プログラムを書いてみる。
- ASRL1::INSTR と ASRL10::INSTR が検出された。それぞれ、COM1,LPT1のことらしい。
- つまり、VISAは正しくインストールされているということか。
- じゃ、何でダメなの?
- VISAリソース名の定数を作成、ASRL1::INSTRになってるが、これじゃダメなのか?
- あぁ、COM1とLPT1が選択出来る。。シリアル構成だから、指定は不要だとおもったんだけど。。
COM1にしたら、受信出来るようになった!
- 良く分かるように、波形チャートを繋いでみよう。
- いちおう、0〜70までちゃんと変化していることが分かる。
- でも何故いちいちゼロに戻るんだろ?
- 良く考えると、空白で区切ってデータを出力してるから、5文字になるのか。。
- 5文字にするとうまく、取り込めるようになった。
- ふーん、でも5文字という指定だけなのに、よくデータの取り違いなしに取り込めるもんだ。
- デリミタを空白にしてるわけじゃないのにね。何か裏でうまくやってくれてる感じだ。
- 送信に関しては「復帰改行定数」を付加することで、うまく行くようになった。
- つまりCRLFだと思うけど、日本語に翻訳しなくても良いんじゃないかなぁ。。
基本的には、LabViewでシリアル入力のプログラムを作るのは、意外に簡単に出来そうなことがわかった。
- だけど、今の表示プログラムのままではPC側プログラムが組み辛いことも分かった。
- 今は周辺装置側のタイミングでデータが送られる。
- でもPC側から操作するなら、やはりPC側からのコマンドでデータを出力するようになっていた方が良さそうだ。
- 今のままじゃ、どうやってデータをやり取りしたら良いか、見当もつかない。。
- とりあえず動くようにはなったが、これじゃ使い物にならないなー。
- ちゃんとデータの受渡し方法を決めておく必要がありそうだ。
それを踏まえて、プログラムを作りなおしてみようか。
[top]
- まず、全てPC側からのコントロールコマンドにより動作するようにする。
- PC側の動作から考えた方が良さそうだな。
- 表示器が繋がっていることを確認する。
- 以下、繰り返し
- 設定ボタンが押されていれば、データを設定
- +ボタンが押されていれば、データをインクリメント
- ーボタンがおされていれが、データをデクリメント
- データを読み出す。
- データの値により文字表示
本体のフローに合わせて、以下のような最低限のコマンドを作ろう。
- リセット
- 文字表示
- データ読出し
- データ設定
- データ値をインクリメント・デクリメントする
コマンド駆動型のプログラムを考えてみる。
- UARTモジュールのHigh Level APIを使えば、そのまま出来そうだ。
| コマンド | パラメータ | 戻り値 | 説明 |
| R | なし | 起動メッセージ表示 | リセット |
| G | なし | カウント値 | カウント値取得 |
| S | カウント値 | カウント値 | カウント値設定 |
| + | なし | カウント値 | カウント値+1 |
| - | なし | カウント値 | カウント値ー1 |
| V | 文字列 | なし | LCDにメッセージ表示 |
- R、リターン、+、リターン、-、リターンっと、お〜、ちゃんと動く、動く。
- ふーん、キー操作でLCDのグラフを増減させられるのは、単純だけど思ったより面白いなぁ。
[top]
- 最初は、起動メッセージを受けるので、VISAの受信モジュールを置いてみたが、I/Oエラーになってしまって、全然うまく行かない。
- バッファの大きさ変えてもダメ。
- 何故だろ?起動時にゴミが出てるのかな。。。
- しょうがないので、最初はリセットを掛けるようにしてみた。
- あっさり動作した。
- でも、タイムアウト時間が長いと何故か反応が鈍い。
- データは最後にCRLFが付いてるから、タイムアウト時間は関係ないはずなんだけどなぁ。
- それに、電球(実行のハイライト、つまりトレースのようなもの)をチェックしたら、ちゃんと動作してるみたいなんだけど。。。
- しかたがないので、タイムアウトを100msに設定した。
以下は、Up,Downボタンを付けてみたもの。
- 液晶画面のバーグラフと、コントロールパネルの動きがちゃんと連動している。
- LabViewは、ちょっとしたプログラムを作るのに、とても便利だということが分かった。
- 評価版は5分しか動作しないけど、インストールして1ヶ月は、ほとんどフル機能を試すことが出来る。
- もっと、値段が安ければなぁ。。。
[top]
[電子工作関連に戻る]
⇒ Disqusの広告がうるさすぎるので基本は非表示にしました