2013年7月30日火曜日

PIC C18 メモ(USART ALFAT 3)

-----------------------------
SDカードに書き込むファイルのルール
-----------------------------
右表のように、FATで検出用に使っている文字を含めると使用できる文字は制限が多い。
装置の条件ファイルは、次のようなルールに

  • フォルダ名は8文字以内
  • ファイル名は8文字以内
  • 条件ファイル内で使える文字は英数字のみ
  • 一項目あたりのデータ長40文字
  • 項目数5項目
  • データ40文字以外のコメント部分の分離(読み飛ばす)
ALFATのファイルリード時は、読み込むバイト数の最大値を指定しないとならない。40文字を超えた分は\rまで読み飛ばすなどいろいろエラー回避策が必要になる。
2007.02.05

この頃は、コマンドレスポンス、ファイルの終端など、特定文字で検出しようと苦労していた様子がうかがえる。ALFATによるシステムの検討はここまで。調査対象はuALFAT、LF62 へと移る。
2013.08.21

PIC C18 メモ(USART, ALFAT 2)

-----------------------------
ALFATの応答
-----------------------------
CH1(上):ALFATからの送信データ
CH2(下):PICの送信文字(ミドリマーカー部分はそのエコー
【注】エコーを返す設定になっているので、PICの送信文字がALFATから返されている。エコーは4msおきに返る。ただ、Rに対するエコーは返されない。コマンド EE 0 でエコーオフに。


[PIC送信]
①R ②RS OK\r ③R ④EE 0\r

[受信]
BL

 GHI Electronics, LLC
----------------------
    ALFAT(TM) 3.12
Z:>RS OK

BL

 GHI Electronics, LLC
----------------------
    ALFAT(TM) 3.12
Z:>EE 0

Z:>

【コマンドが一挙に送られた/エコーオフ】

[PIC送信]
①A:\rCD KD9001\rLF\rZ:\r

[受信]
A:>
A:\KD9001>
. <DIR>
.. <DIR>
LED001.KDC  0xA0 160
LED001A.KDC  0xC8 200
LED001B.KDC  0xC8 200
LED001C.KDC  0xC8 200
LED001D.KDC  0xC8 200
LED001E.KDC  0xC8 200
LED001F.KDC  0xC8 200
A:\KD9001>

Z:>

【コマンドを区切った/エコーオフ】

[PIC送信]
①A:\r ②CD KD9001\r ③LF\r ④Z:\r

[受信]
A:>
A:\KD9001>
. <DIR>
.. <DIR>
LED001.KDC  0xA0 160
LED001A.KDC  0xC8 200
LED001B.KDC  0xC8 200
LED001C.KDC  0xC8 200
LED001D.KDC  0xC8 200
LED001E.KDC  0xC8 200
LED001F.KDC  0xC8 200
A:\KD9001>

Z:>

2006.10.12

-----------------------------
電源オン時の波形
-----------------------------
CH1(上):ALFATからの送信データ
CH2(下):PICの送信文字
  • 電源オンにて、ALFATがBLを送ってくる
  • PICは初期化後、LCDなどの設定とタイマに150ms位かけている
  • その後、ALFATへRを送って応答があるが、すでに受信オーバーランエラーが起きている
  • 初期化時点では受信ディエーブル(RCSTAbits.CREN)するか
  • 送信前に再度RCSTAbits.CRENを一旦クリアする必要がある
-----------------------------
ALFATオープンタイミング
-----------------------------
  • ALFATは必要になった時にオープンする
  • どこにいるかわからなくならないように必ずZ:>に戻る
  • かまたは、どこにあってもいいように問い合わせして始める


2006.12.02

PIC C18 メモ(USART, ALFAT)

-----------------------------
FAT-001
-----------------------------
  • FAT-001は、GHI Electronics社のALFATを用いた評価ボード
  • Windows互換ファイルシステム(FAT)を備える
  • UART、SPI、I2Cなどのインターフェイスで使え、PICで制御可能
  • SDカードタイプを購入した
  • ALFATは70KB/SECの最大書き込みスピード
  • ファイル名にはパスを含むことができない
  • いつ電源を切られてもいいように、常にZ:>のプロンプトに待機させておくこと
  • JP4は2-3,5-6 を 1-2,5-6 ショートへ変更する
FAT-001の実験の後、uALFATのチップだけ購入し基板を製作した。実験を始めようとしたところで中断(2007.02)。そのまま。

-----------------------------
getcUSART
-----------------------------
  • getcUSART で読み出した時、フラグはクリアされるが、RCREG(受信バッファ)には同じデータが残っている
  • プロンプト">"出現を検出するようなプログラムは、データが残っていると、待ちループを突き抜けてしまうので、必ずクリアすること
  • 文字が正しく取得できない時は、RCSTAを確認すること(オーバーランエラー発生)
  • 受信した文字をすべて読みだしておくか、RCSTA の受信イネーブルを0->1にしてリセットすればいい
  • DataRdyUSART(); を getcUSARTの前に実行すること。

-----------------------------
putsUSART
-----------------------------
  • nullまで送ってしまうので使えない・・・ので自作関数を作成した
2006.10.12 - 2013.07.26

【注】GHIElectronics社は、ALFATを新しいデザインに使用しないよう勧告している。

PIC C18 メモ ( sprintf )

-----------------------------
SPRINTF
-----------------------------
数値を表示用のASCIIへ変換して右詰数値にするだけなら、sprintf  関数を使う。

  • フラグで、左詰、+符号有り無しなど
  • 下の例にはないが、変換方法の指定で16進表示なども出来る
  • いろいろありすぎて取っつきにくい

void main( void)
{
  int m = 123;
  sprintf( buff, (far rom char *) "%u", m );   //123
  sprintf( buff, (far rom char *) "%5u", m );  //__123
  sprintf( buff, (far rom char *) "%05u", m );//00123
}
2011.06.16


「C言語 sprintf カンマ区切り」でググってみたが、カンマをはさむのは簡単ではない。sprintf 関数では、整数値の好きな位置に小数点を挿入することはできなさそう。小数点を好きな位置に配する関数は無駄ではなかったようだ。

2013.07.30

2013年7月26日金曜日

PIC C18 (USART, LF62 書き込み)

-----------------------------
LF62 連続書込み試験(2010.01.07)
-----------------------------
USBメモリへLF62を使って、40文字5行の200バイトを一秒毎に書き込むテスト。
  • LF62 + PIC
  • USART:38400boud, 200byte
  • ファイルオープン、書込み、クローズ(メモリを抜かれた場合の為)の繰り返し
  • USBメモリ:ELECOM 1GB MF-AU201GGT
  • キーワード:組み込み機器, PIC, C18, ファイルシステム, FAT

①WriteFile Openオープン待ち70ms
②WriteFile70ms
③WriteFile Closeクローズ待ち390ms

ファイルオープンコマンドを書いてから、クローズ完了しプロンプトが返るまで 530 msだ。クローズのプロンプトが返るまでは、再書込みはできないので、この間は待つしかない。
データロガーの場合は、この530msの間もデータが一定時間間隔で溜まってくる。


【USART: 9600boud】
PIC-LF62 の転送速度を 9600boud にした場合が左図。
書き込みにかかっている時間は、データ個数で決まってくる。効率の良いデータ個数(セクタ単位512バイト)と、書込み間隔の設定が重要なようだ。USARTのボーレートは直接関係しない。
【問題点】
  • ボーレートを変えた場合、PIC側とLF62側のリセットのタイミングの問題の解決
  • USBメモリの認識(LF62)まで1分かかる
  • メモリフルの検出方法(残容量を時々調べるか、最初に残容量を調べて自分で積算する)
  • 電源をいつ切られてもいいように、割り込みでファイルクローズ処理する必要がある
  • フラッシュメモリには寿命がある
    (特定のメモリセルへ集中しないような工夫のされたUSBメモリもある)
-----------------------------
PCとUSBメモリ間で転送時間を測ってみた(2010.01.21)
-----------------------------
  • 500バイトのテキストファイルを50個作って転送した
  • PC →USB メモリへ書き込み:約5.3秒
  • USB →PCへ読込:1秒以内
  • 500バイトなので、1セクタ ごとにファイルコピーされた(1セクタ=512バイト)
  • 10セクタ/秒で、5kB/s
  • LF62では、ファイルクローズ処理に200ms以上かかっているのでシーケンシャルに考えると1秒間に4セクタ程度である
-----------------------------
断片化(2010.01.21)
-----------------------------
断片化(ファイル削除が繰り返された)の進んだデバイスは空セクタの検索に時間がかかる。当然メーカーや品種でも違ってくる。データロガーのような使い方のフラッシュメモリは、ファイルの削除はまとめて行って、再フォーマットしてから再使用する位がいいのかも。

2010.01.07 - 2010.01.21

2013年7月24日水曜日

PIC C18 メモ(USART, LF62)

-----------------------------
L&F社のLF62
-----------------------------
  • USBフラッシュメモリ用の組込み用途向けリーダライタ
  • USBインターフェイスとファイルシステムを内蔵していて、簡単なコマンドでマイコン側から使用する事ができる
  • マイコンとのインタフェースはRS-232CかTTLを選択できる
  • 2007年12月購入
-----------------------------
使途(検査機器)
-----------------------------
  • シリアルポートを使って接続しているが、パソコンを持ち込めない場合の代替
  • パソコンで編集した条件ファイルの受渡しに使用する
  • 測定結果をUSBメモリへ保存したい(ログ機能)
-----------------------------
PIC USART 経由の読み込み
-----------------------------
  1. 設定行数読み込んだら後は無視する方法
    • USART オーバーランエラーが発生するので次のアクション時にクリア必須
  2. 終端文字が出るまで、空読みする
    • もしこなかったらフリーズする
  3. LF62が連続してコマンドを受け付ける
    • ファイル転送中であっても次のコマンドを受け付ける
    • CTSを見てもレディになっている
    • 勝手に受信を打ち切った場合、どのコマンドに対する返答なのか分からなくなる
従って
  • 文字数を制限するなどして勝手に受信打ち切りしてはならない
  • 受信文字が空になるまで、一定時間待つ
  • プロンプトが見つかるまで受信する
  • デイレクトリの表示コマンドなどの場合は、受信文字の個数は分からない
  • 必ずしも受信時にCR,LFが入っているとは限らない
  • コマンドに対する最終結果の文字がプロンプト
  • タイムアボートは必須(ただ何秒待てばいいのか)
-----------------------------
バイト数で判断すればいいのでは(2013.07.26追記)
-----------------------------
LF62の返してくるプロンプトを見つけようとしていたが、ファイル受信の場合はバイト数で判断すればいいのでは、とLF62の説明書を眺めながら思った。間違って大きなファイルを指定した場合など拒否できるので、時間制限を短く設定できる。

2008.01.08 - 2013.07.26

PIC C18 メモ(CSV形式で保存した場合)

----------------------------
CSV形式で保存した場合
----------------------------
テキスト形式の条件ファイルを、「メモ帳」、「エクセル」どちらで編集してもいいようにしたい場合。

【1】.TXTのファイルを「メモ帳」で作成
1234,5678,9012
1234,5678,9012
/

【2】.TXTのファイルの中
0000 31 32 33 34 2C 35 36 37 38 2C 39 30 31 32 0D 0A 1234,5678,9012..
0010 31 32 33 34 2C 35 36 37 38 2C 39 30 31 32 0D 0A 1234,5678,9012..
0020 2F                                              /             

【3】この.TXTファイルをEXCELに読み込んで.CSV形式にして保存したとき
0000 31 32 33 34 2C 35 36 37 38 2C 39 30 31 32 0D 0A 1234,5678,9012..
0010 31 32 33 34 2C 35 36 37 38 2C 39 30 31 32 0D 0A 1234,5678,9012..
0020 2F 2C 2C 0D 0A                                  /,,..         

【4】「メモ帳」で再度開くと
1234,5678,9012
1234,5678,9012
/,,         
  • 条件ファイルの終端記号として"/"を使おうと考えた
  • エクセルはセルの範囲で認識されるため、3行目にカンマと0D0Aが付加される
  • "/"をファイルの終端として使うならば、後のデータを無視することに

2008.01.09

2013年7月23日火曜日

PIC C18 メモ( FIFO, Queue )

-----------------------------
キュー( Queue )
-----------------------------
[ ],[ ],[Head],[ ],[ ],[ ],[ ],[ ],[Tail],[ ],[ ],
  • デキュー( Dequeue ): Head から取り出す(Headは右へ移動)
  • エンキュー( Enqueue ): Tail へ挿入する(Tail は右へ移動)
【初期化】
  • Head = Tail = 0 とクリアした後、メモリを確保
  • Head = Tail は空
  • Head - Tail = 1 は満杯
【エンキュー,データ書き込み】
if((Tail+1) % point == Head ){
  return FULL;
}
Data[ Tail ] = Value;
Tail = ( Tail + 1 ) % point;
return SUCCES;

【デキュー,データ読み出し】
if( Tail == Head ){
  return EMPTY;

}
*ReturnValue = Data[ Head ];
Head=( Tail + 1 ) % point;

2011.01.04

2013年7月18日木曜日

PIC C18 メモ(LCD コントラスト調整)

キャラクタディスプレイのコントラスト調整は、10kΩの半固定抵抗が薦められている。ここをPWMを使用して発生させた電圧で置き換えてみた。
調べもせず、単なるCR時定数回路だけでやったら失敗。右図のようにシンク回路が必要で、PNPトランジスタを追加することになった。
  • PC2402-A POWERTIP社製
  • 24文字 x 2行 キャラクタディスプレイ
  • 周波数は 7.2kHz
  • 初期値は 1.1V
  • ボタン操作で 64段階の調整(0.5~3V)
  • 図の J4 は無関係
ただ、デバッグ用のICD2を停止すると、LCD 表示も消えてしまい(PWM も停止するから)、これまでと違ってすごく不便に感じた。デバッグ時は、停止をかけても表示が残っていた方がいい。
2011.02 - 2013.07.18

PIC C18 メモ(ブザー音発生)

【条件】
  • PIC18F4520, Clock 4MHz
  • ブザー駆動用の波形 5kHz を Timer2/PWM を使用して作る
  • 圧電サウンダ他励振タイプ(PKM17EPPH4001-B0, Murata)
【設定】
//PWM for Buzzer
#define BZ_PRIOD 220
#define BZ_DUTY 440
OpenTimer2( TIMER_INT_OFF & T2_PS_1_1 & T2_POST_1_1 );
OpenPWM1( BZ_PRIOD );
SetDCPWM1( BZ_DUTY );
//SetDCPWM2( 0 );      //(*1)
Delay10KTCYx( 5 );     //50ms BZ ON
SetDCPWM1( 0 );        //BZ OFF
  • RC1,RC2の両ポートともタイマ2で共通設定され、片側のみ使うことは出来ない
  • (*1)拡張モードでない場合はSetDCPWM2( 0 )を実行してしまうとRC1ピンの方もオンオフできなくなる
【計算式】
RSフリップフロップのRに「周期」カウンタ、Sに「デューティ」カウンタを備え、交互にトリガするような構成である。ここでの設定は、プリスケーラは1、同じクロック源をカウントするわけなので、最大のデューティ分解能の 10bit が活かせるのは最大周期の時である。周期設定値 PR2=220 に対して10bitの分解能でデューティ設定できるわけではないので注意。

周期は
= ( 220 + 1 ) x 4 x 0.25usec
= 221us ( = 4.525kHz )

デューティは
= 440 x 0.25usec
= 110us ( = 50% )
【実施】
実際使うときは、この波形を断続して左図のように、ピピピピと鳴るような使い方とした。回数指定してモード毎に鳴り分ける。
2005.11 - 2013.07.18

PIC C18 メモ(タイマの選択)


Module Counter Prescaler Postscaler Remark
Timer0 16bit 2,4,8,16,32,64,128,256 最長のタイマ
Timer1 16bit 1,2,4,8
Timer2 8bit 1,4,16 1:16 PWM 用
Timer3 16bit 1,2,4,8 Timer1 と同じ

2008.04.09
タイマの最長時間(Timer2は単純なタイマには使わない)
Clock Timer0 Timer1,3 Timer2
4MHz 16.7sec 524msec
10MHz 6.7sec 209msec
16MHz 4.2sec 131msec
20MHz 3.3sec 104msec
2013.07.18

2013年7月17日水曜日

PIC C18 メモ( SPI )

----------------------------
再編集します
----------------------------
このページの内容は、他にもメモが出てきたので、次のようにページを分割してバージョンをさかのぼって再編集することにした。
他のSPI関連ページは

2013.08.08

PIC C18 メモ(RAM の不足)

-----------------------------
RAM の不足( Error - section 'f1name' can not fit the section. )
-----------------------------
【例1】コンパイルエラー
#pragma udata f1name
static char fname1[8][16];
static char fname2[9][16];
  • これは、256バイト超でデータメモリのバンクをまたがってしまい、リンカエラーでる
【例2】
static char fname1[8][16];
static char fname2[8][16];
  • これは、256バイトに収まっているのでOK  #pragma 無くてもいい。
【例3】
#pragma udata f1name
static char fname1[16][16];
#pragma udata f2name
static char fname2[9][16];
  • これは、別々のバンクに配置され、OK。
  • 多くのメモリを使用する場合は、予めセクションを指定して配置しろという事か
【例4】
#pragma idata f1name
static char fname1[16][16] = { 0 };
#pragma udata f2name
static char fname2[9][16];
  •  idata セクションにして、ゼロで初期化できた
    (130バイト位プログラムメモリが増えた)
  •  idata セクションにしても、={ 0 } が無いと何も起こらない
整理にあたって、何度か目を通すも、わざわざ打ち込んでページを起こすかどうか迷った個所。結果的に、メモの内容より書き足すハメに。

2006.10 - 2013.07.17

【おまけ】
データメモリの配置を確認するには、Watch ウィンドウでもアドレスを知ることができるが、
[ File ]->[ Open ]->[ Map Files ]
で、全体の配置を示すマップファイルを表示できる。

2013年7月16日火曜日

PIC C18 メモ( I2C )

-----------------------------
I2C 関数
-----------------------------
void DataTrans(unsigned char data)
{
  WriteI2C(data);  //データ出力実行
  IdleI2C();       //バスアイドル待ち
}
void Dev_Cmnd( char sub_adrs, char data )
{
  StartI2C();            //スタート条件出力
  while(SSPCON2bits.SEN);//出力完了待ち
  DataTrans( Chip );     //チップアドレス出力
  DataTrans( sub_adrs ); //サブアドレス出力
  DataTrans( data );     //書き込みデータ出力
  StopI2C();             //ストップ条件出力(*1)
  while(SSPCON2bits.PEN);//出力完了待ち
}
char ReadSqc( char sub_adrs )
{
  char rd;
  StartI2C();            //スタート条件出力
  while(SSPCON2bits.SEN);//出力完了待ち
  DataTrans( Chip );     //チップアドレス出力
  DataTrans( sub_adrs ); //サブアドレス出力
  StartI2C();            //スタート条件出力(*2)
  while(SSPCON2bits.SEN);//出力完了待ち
  DataTrans( Chip );
  rd = ReadI2C();
  StopI2C();             //ストップ条件出力
  while(SSPCON2bits.PEN);//出力完了待ち
  return rd;
}
  • これは、I2Cインターフェイスをもつデバイスのテスト用のもので、通信が成功しているかどうかは、別の手段で検査する
  • 送信前と後にアイドル確認している例があるが今回の場合は、一方的に送信するだけなので入れてない
  • (*1)ストップ条件出力と完了待ちは必須、無いと正しく書けない
  • 読み込みは、指定されたレジスタアドレスからデータを読み込む
  • (*2)読み込みは、ReadI2Cの前に再度スタートコンディションが必要
2009.02 - 2013.07.16

PIC C18 メモ( I2C 設定)

-----------------------------
I2C 設定( PIC18F2520 )
-----------------------------
//I2C Init( 100kHz, マスターモード )
  OpenI2C(MASTER, SLEW_OFF);
  SSPADD = 24;//(10M/(4*0.1)-1=24)
//I2C Init( 400kHz, マスターモード )
  OpenI2C(MASTER, SLEW_ON);
  SSPADD = 9;  //(16M/(4*0.400)-1=9)

  • 100kHz はOK
  • 400kHz で使った時、実測波形 360kHz 程度だった(333kHz以上は適合しない)
  • IOポートの方向は、マスターは「出力」に、スレーブは「入力」に設定する
-----------------------------
プルアップ抵抗
-----------------------------
  • 「送信」だけなら単独でテストできるが、SDA,SCL 両方ともプルアップ必要
  • 5Vの場合、2~5kΩ
    3Vの場合、1~3kΩ
  • 抵抗値が小さいとシンク電流制限を超えるし、大き過ぎると波形が鈍る
2009.02 - 2013.07.16

PIC C18 メモ(剰余)

----------------------------
ループカウンタとして
----------------------------

8 ÷ 5 = 1 余り 3
7 ÷ 5 = 1 余り 2
6 ÷ 5 = 1 余り 1
5 ÷ 5 = 1 余り 0
4 ÷ 5 = 0 余り 4
3 ÷ 5 = 0 余り 3
2 ÷ 5 = 0 余り 2
1 ÷ 5 = 0 余り 1

上の例では剰余は、0~4のループカウンタに使える事が分かる。「剰余」は使ったことがなかったが、サイクリックバッファのキューの説明に登場。ただ値によっては上下限と比較し、元に戻す必要(クリア)がでてきくるのでループカウンタは「アップダウンカウンタ関数」でいい、とした。

2011.01- 2013.07.16

2013年7月15日月曜日

PIC C18 メモ(LCD 用の関数いろいろ)

-----------------------------
配置
-----------------------------
  • カーソル先頭位置を基準とした表現方法にした
  • 各行の先頭位置を #define
  • 行の指定と、先頭からのオフセット値を引数でもらう
  • カーソル表示オンオフ関数
  • 表示用文字や、カーソル位置のデータを構造体化して用いる
-----------------------------
文字表示用
-----------------------------
  • RAM文字列とROM文字列用の関数が必要
  • 空白で指定数埋める関数
  • LCD に書き込みながら、文字数をカウントして戻り値で返す関数
  • 指定行を空白で埋める関数
  • 数値を、指定のフォーマットの文字列に変換する関数
初期画面を作る関数は、システム毎、モード毎に異なるが、以上のような関数を基本にして作っている。表示書き換えの際は、残りを空白で埋めて再度上書きするケースもある。これは消し忘れ、消し残りをなくすために、項目毎に確実に画面クリアした方がいい為。

- 2013.07.16

PIC C18 メモ(SPI 設定)

-----------------------------
SPI 設定( 2008.12.03 )
-----------------------------
「クロックは、通常Hで、立ち上がりエッジで取り込み」に修正した。TPIC6C595 8bit Shift Regisster  のタイミングに合わせた。MCP3201 12bitA/D Converter With SPI にも合致する。

マスタモード側の設定( PIC18F4520 )は次の通り。
OpenSPI( SPI_FOSC_16, MODE_11, SMPMID ); //SSPEN,MASTER,CKE=0,CKP=1,fosc/16



MODECKPCKE
0001
0100
1011
1110


CKP: クロック極性は 0 で アイドル時 L レベル
CKE: アイドルから反転で始まるのが 0

-----------------------------
【おまけ】MCP32xx A/D コンバータ使用時の注意点
-----------------------------
最低クロック速度(変換時間)の制限があり、1.2ms 以内にリードアウトすること。SPI クロック設定は、遅くする方にも注意が必要。
2013.07.15

2013年7月11日木曜日

PIC C18 メモ(LCD 行のスクロール)

-----------------------------
LCD 表示の縦スクロール
-----------------------------
【条件】
  • 20文字x4行の LCD の下3行を縦スクロールする
  • 下図のようなイメージ
  • 測定項目名と、測定値(簡略化した例)

【二案】
  1. 方法1
    • LCD表示イメージで保存して、スクロールは、そのまま再表示するだけ
    • 一文字ずつ表示する時にメモリへも書き込む
    • 消費メモリが表示文字数分必要で、元々文字列データ部分が二重になって無駄
    • ただし、LCDのメモリが使えれば、無駄はない(読み込み時間がかかるかも)
  2. 方法2
    • 測定値は、原データで保存して、表示の都度表示用文字列に変換する
    • 表示の為の前回、前々回の測定値を残すメモリがあればいい
    • 変換処理時間分増える
どちらの方法を採るかは、表示文字に変換するまでの処理時間とメモリの手当。
  • 方法2を採用した
  • LCDの書込み待ち時間が長いので、都度計算してもトータルでは大差ない
  • ループカウンタが要る
  • メモリ読み出し順を変えることで、表示文字が上の行へ移動する
【手順】
  • 一発目は、画面をクリアして最下行へ書き込む
  • 次から、測定開始にて上行へ書き換えてスクロール
  • 予め表示する項目(測定項目名)の書き込み
  • 測定完了後表示する項目(測定値と判定結果)の書き込み
2010.04 - 2013.07.12

PIC C18 メモ(LCD 画面上で編集)

-----------------------------
LCD 画面の表示数値の編集
-----------------------------
【条件】
  • 20文字 x 4行の LCD
  • 表示されている数値をアップダウンで増減して編集したい
  • 該当箇所までカーソルを送る
  • 小数点付加前の整数値で10の位と、1の位を増減(100,10の位でも可)
  • 必要なボタンは図のように6つあるといいが、最低4つ(UP,DOWN,NEXT,ENTER)でもOK
【手順】
  1. AddValue = 0
  2. カーソル位置に対応する増減分(10 or 1)の取得
  3. UP ボタン押下時 AddValue = AddValue +10( or +1)
    DOWN ボタン押下時 AddValue =  AddValue -10( or -1)
  4. 加算結果が制限範囲内(下図)かチェックし、超えたら制限値を AddValue に代入する
  5. 確定したら(カーソルが次の項目に移動するか、次の画面へ切り替わったら)
    保存する CurrentValue = CurrentValue + AddValue



【制限範囲のチェック】
  • 上図は、編集される側の値( CurrentValue )が、測定値に対する上下限判定値として用いられる場合を表している。この範囲内に測定値があったら"良品"として判定する
  • 引数として、増減値とUP/DOWNのサインをもらう
  • UP/DOWNの違いは、加算部分は増減値の極性が違うだけなので共通にできるが、値をチェックする部分は別れる
  • サインに応じて、上図のように AddValue の制限が必要

カーソル移動で編集できる項目は、複数になる。増減値やカーソル位置などの関連情報は構造体を使って一括管理する。



2008.11 - 2013.07.12

2013年7月10日水曜日

PIC C18 メモ(A/D測定値の表示)

-----------------------------
A/D変換後の測定値を LCD に表示(初期の考え方)
-----------------------------
A/D 変換後の測定値を LCD に表示するために、書式を整える必要がある。

  • itoa 関数の戻り値は、左詰文字列である
  • itoa i関数は - の時だけ符号があり、+の場合は無い
  • LCD 表示は右詰にしたい
  • 極性の表示位置は、左側の固定位置にしたい
  • 小数点があるので、ゼロで埋める(ゼロフィル)
  • 上位桁のゼロ表示はせず空白(ブランキング)
  • 測定レンジに応じて小数点位置が変わる
手順
  1. itoa 戻り値の文字列をポインタを末尾まで送り文字数を調べる
  2. 表示用の配列の末尾にポインタを進め、NULLを入れておく
  3. 両ポインタをデクリメントしながら表示用配列へ一文字ずつコピー
  4. 小数点位置に来たらピリオドを表示用配列へ入れる
  5. 数値文字が終わったら、ゼロを書き込むか、空白を書き込む
  6. 表示用配列が先頭に戻るまで、繰り返す
  7. 表示用配列(文字列)を返す

-----------------------------
上の改良形
-----------------------------
しばらく上のやり方の関数を使ったが、すごく難しくしていることに気がつき、改めた。表示用のフォーマットに、数値を上書きすればいいだけのことだ。上の手順の 4, 5 が簡略化される。


  • 表示用配列に表示用フォームをコピーする
  • itoa 戻り値を表示用配列に上書きする
  • - の符号がでてきたら、それは先頭に記入する
  • 上はポインタをデクリメントしながら同時に処理する
最終形は、メモリ使用量150バイトくらい減った。sprintf 関数がこのような機能を持っている事を知ったのは、この形が出来た頃。この関数の作成は、着手から何枚もの手書きのメモが残っていたが、まとめるとほんのこれだけ。メモの内容は、ほとんどが「配列」やポインタ経由の受け渡しとなどの勉強と分岐条件の検証だった。

2006.12 - 2013.07.11

2013年7月8日月曜日

PIC C18 メモ(リンカファイルの最新版)

以前は、拡張モード、デバックモードなどリンカファイルが分かれていたが、現在は統合されて 18Fxxx_g.lkr となっている。最新版のC18(V3.45) は、インストールされるフォルダ構成も変わったみたい。次の場所。

C:\Program Files (x86)\Microchip\mplabc18\v3.45\bin\LKR

2013年7月7日日曜日

PIC C18 メモ(引数に配列や構造体)

-----------------------------
配列を return で返したい(戻り値)場合
-----------------------------
typedef struct{ char ccc[3]; }ary_t;
---------
ary_t Function( void )
{
  ary_t c = { 2,3,4,};
  return c;

}
---------
void Main( void )
{
  ary_t x;

  x = Function();

}
  • 配列を構造体に型宣言して使用する
-----------------------------
配列を引数にしたいとき(構造体で渡す)
-----------------------------
typedef struct{ char ccc[3]; }ary_t;
---------
void Function( ary_t x )
{
  ary_t y = x;
}
---------
void Main( void )
{
  ary_t x;

  Function( x );

}
  • 構造体にすることで、関数の引数、戻り値として使え、この場合は値が渡される
  • 呼び出した側の関数の構造体の内容は変化しない
-----------------------------
配列を直接引数にした時
-----------------------------
void Function( char ary )
{
  ary[ 0 ] = 9;
  ary[ 1 ] = 8;
  ary[ 2 ] = 7;
}
---------
void Main( void )
{
  char fff[ 3 ]={ 4,5,6, };

  Function( fff );
}
  • 上の例だと、fff は 9,8,7 に書き換わる
  • 配列を引数にすると、先頭アドレスが渡され(ポインタ渡し)るので、元の値が書き換わる
-----------------------------
同じ型の構造体は、コピーできる
-----------------------------
  • 配列は、 = で内容をコピーできないので、memcpy のような関数を使うことになる
  • 同じ構造体の場合は、 = を使って内容をコピーできる
------------------------------
構造体を関数に渡すときの効率
------------------------------
  • 関数からの戻り値、引数として便利に使えるが、値渡しであるがゆえに
  • 構造体データを引数にしたら、RAM消費増
  • 一旦必要な分だけローカル変数に代入して渡すと、RAM消費は元に戻り、ROM消費増
  • プログラムの見易さ、分かり易さを優先して
2008.04 - 2013.07.12

PIC C18 メモ(ハマった)

-----------------------------
()が抜けていてもコンパイルは通る
-----------------------------
if function() = ture then{  //OK
  exec();
}
if function = ture then{    //NG
  exec();
}
  • ()が抜けていてもコンパイルは通る
  • 動いてしまうのでまずい
  • しょっちゅうやってしまう
-----------------------------
グローバル変数
-----------------------------
  • 低レベル関数でグローバル変数を使うとあとで訳が分からなくなる
    (いつどこで書き換えられているか不明に)
  • できれば、高いレベルの関数から引数で渡す形をとるように
-----------------------------
exturn データが正しく読めないトラブル
-----------------------------
exturn char xxx[];             //NG
exturn const rom char xxxx[];  //OK
  • プロトタイプ宣言に"const rom" が抜けていると正しくデータが読めずバグる
  • コンパイルは通るし、動く時もあるのでまずい
  • 常に、ROMか、RAMか、を意識して
-----------------------------
Error[ 1302 ]
-----------------------------
  • コンパイル中に、呼び出されたサブルーチンが見つからない
  • 先に、その関数を使用することを宣言する必要がある(プロトタイピングが必要)
  • 呼び出す関数が自分より後ろにあったらダメ、入れ換えればエラーは消える
-----------------------------
ポインタの注意点
-----------------------------
char *str         //(*1)
str[ n ] = *str;  //(*2)
*pt = str[ n ];   //(*3)
  • (*1)汎用ポインタというらしい。文字列の配列を準備する場合にも使えるが、長さが分からないまま使わない
  • (*2)自分がわかっている長さに、ポインタ経由でもらうような場合はいいが
  • (*3)アドレスを取得するだけならいいが、ポインタ経由で書き換える場合注意
  • 文字列を使い始めるとき時は、必ず長さを指定して str[ n ] とすること
  • 扱う側が、文字列と分かっている場合は、NULLで終端検出すればいいので気にしない
-----------------------------
半日費やしたポインタのミス
-----------------------------
n = *pt;     //OK
n = *pt++;   //NG ポインタが進むだけで n は先の値のまま
n = *(pt++); //NG 上と同じ

n = *(++pt); //OK ポインタが進んで次の値が現れる
  • pt++ (ポインタのインクリメント)の部分を、サブルーチン側へアドレス渡しとして移植した時、単純に  *pt++ としたミス。思った通り、ポインタを進めて値を取り出せない
  • 構造体、共用体が使えるようになってから、ポインタ経由の使い方は減ったように思う(下位関数では多用している)
-----------------------------
大小比較するときの注意
-----------------------------
0xFF ≦ 0x00 //char だと TRUE
0xFF ≦ 0x00 //unsigned char だと FALSE
  • 0xFF はマイナス1なので TRUE である
  • バイポーラのアナログ値の比較は注意
-----------------------------
コメントに使えない文字(5C問題)
-----------------------------
SHIFT-JISコードに 5C が含まれる漢字
[―](815C) [ソ](835C) [Ы](845C) [Ⅸ](875C) [噂](895C)
[浬](8A5C) [欺](8B5C) [圭](8C5C) [構](8D5C) [蚕](8E5C)
[十](8F5C) [申](905C) [曾](915C) [箪](925C) [貼](935C)
[能](945C) [表](955C) [暴](965C) [予](975C) [禄](985C)
  • ○○表で改行するコメントでこの問題にハマる
  • 能も危ない、「分解能」とか
  • 症状は、「次の行を認識してくれない」ので、デバッガで追うとジャンプする
-----------------------------
型のトラブルいろいろ
-----------------------------
整理してみて分かったのは、その多くが、ポインタが示す先が RAM, ROMの どちらなのかというトラブルに時間を費やしている。エラーにはならずコンパイル成功してしまうので、最初からあいまいな表現を避けて、作り込み段階で問題が現れやすいようにしておくこと。
特にポインタのトラブルは、プログラムメモリーダンプリストと、Wacth, Local のウィンドウでの動きを見るとよい。とんでもないアドレスを指していたりする。「急がば回れ」。

-----------------------------
正しく足し算してくれない
-----------------------------
#define CUR1 0x14 | 0x80
#define CUR2 0x80 | 0x14
--------
char ccc;
ccc=CUR1+10;//c1=9E,正
ccc=CUR1+11;//c2=9F,正
ccc=CUR1+12;//c2=9C,誤A0だろ

ccc=CUR2+10;//c1=9E,正
ccc=CUR2+11;//c2=9F,正
ccc=CUR2+12;//c2=A0,正

LCDカーソル位置の定義で、足し算が思った通りにできていないことでハマった。「コンパイラのバグ」としてメモ(2008.12)されていたが、今回の再検証で、#define での順番を入れ換えるか、#define 部分を()でくくればいいことが分かった(演算子の優先順の問題だ)。

-----------------------------
RAM 使用量が突然増える
-----------------------------
#ifdef _DA_ADJUST
  #define ADJ_V 20
  #define ADJ_C -16

#else
  #define ADJ_V 0
  #define ADJ_C 0

#endif
----------
void daout (int adj)
{
int dada;
  dada = data + ADJ_V;
  dada = data + ADJ_C;
}

これも「コンパイラのバグ」としてメモ(2008.12)されていたが、再検証はしていない。#define の値が入っている場合に比べて、ゼロの場合、RAM 使用量が18バイト増えるという現象。理由は不明。
作り込みが進んで来たら、RAMと、ROM 使用量は都度チェックするようにしている。減らす事ばかりに注力してしまってもいけないが、関数の追加等の直前の作業内容のチェックに。
リリースする時は、必ずメモしておく。コンパイラのバージョンが変わると、使用量が変わることがある。それによる実害はこれまでに無い。

-----------------------------
ICD2 Busy が長引く(2007.12.28)
-----------------------------
  • USART 受信部分のデバッグ中、文字数制限が正しく実行されないまま
  • デバッガを Holt にした後も、暴走していた可能性有り
  • 文字数制限が機能したらこの問題は消えた

-----------------------------
ソースファイルが全て(2013.08.09)
-----------------------------
今回の「メモの整理」で痛感。残っている資料は、時系列で並べ遡っていかないと訳が分からなくなる。なによりソースコードが全て。
2006.10 - 2013.07.09

2013年7月6日土曜日

PIC C18 メモ(匿名構造体)

-----------------------------
匿名構造体・共用体
-----------------------------
union{ struct{ int a; int b; }; char c; }zzz;        //タグとメンバー名を省略
union foo{ struct{ int a; int b; }xxx; char c; }yyy; //タグ foo メンバー名 xxx

char c;
zzz.a = c;      //OK
yyy.a = c;      //Error [1205] unknown member 'a' in 'foo'
yyy.xxx.a = c;  //OK
  • メンバー名を省略するとコンパイラが勝手に名前をつけていて管理、メンバー名を省略してダイレクトに書ける
2010.05 - 2013.07.06

2013年7月5日金曜日

PIC C18 メモ(構造体へのアクセス)

-----------------------------
構造体のメンバーへアクセス "->"
-----------------------------
typedef struct{ char ccc; int iii;}test_t;
---------
void Function( test_t *zz)
{
  zz->ccc = 8;    //3 を 8 へ書き換えた
}
---------
void Main( void )
{
  test_t zz = { 3,500 };
  Function( &zz );
}
---------
  • 構造体をポインタでもらい、そのメンバーの値を書き換えた。受ける側の関数も同じ型のポインタを必要とする。zz->ccc は、(*zz).ccc と同義
-----------------------------
構造体の配列へアクセス
-----------------------------
typedef struct{ char ccc; int iii; }test_t;
---------
void Function( test_t *zz )
{
  zz->ccc = 8;    // 4 を 8 へ書き換える
}
---------
void Main( void )
{
  test_t zz[ 2 ] = {{ 3,500, },{ 4,600, },};
  test_t *pt;


  pt = zz;        //zz[0]を指す
  pt++;           //zz[1]を指す
  Function( pt ); //Function( &zz[1] )と同じ
}
---------
  • この場合は、データの配列全体ではなく、構造体ひとつを渡している
-----------------------------
構造体のデータを渡す
-----------------------------
typedef struct{ char ccc; int iii;}test_t;
---------
void Function( int z )
{

}
---------
void Main( void ) {
  test_t zz[ 2 ] = {{ 3,500, },{ 4,600, },};

  Function( zz[1].iii );
}
---------
  • 値を渡すだけなら、もらう関数側は、引数の型を合わせるだけ
-----------------------------
共用体のメンバーへのアクセス
-----------------------------
union{ short long count; char byte[ 3 ]; }cat =0x123456;
---------
char Function1( char byte )
{

}
---------
char Function2( char *byte )
{
byte = 0x43;  //0x12 を 0x43 へ書き換えた
}
---------
void Exampl( void )< br/> {
  char *ptc;
  short long *pts;

  Function1( cat.byte[ 0 ] );  //0x56 を渡す
  Function2( &cat.byte[ 2 ] ); //0x12 の入ったアドレスを渡す
  ptc = &cat.byte[ 0 ];
  pts = &cat.count;            //ptc と同じアドレスが入る
}
---------
  • Function1のケースは、関数へ直接データを渡す(値渡し)。
  • Function2のケースは、関数へアドレスを渡して書き換えているので、相手先はポインタ。
  • この場合の、short long は、リトルインディアンなのでメモリ内の配置は、[0],[1],[2] のアドレス順に 0x56,0x34,0x12 のデータが配置されている
2008.04 - 2013.07.05

2013年7月4日木曜日

PIC C18 メモ(Error 1131 )

-----------------------------
型の不一致 Error 1131
-----------------------------
typedef struct{ char ccc; int iii;}test_t;
test_t sdata={0x7F,0x7FFF,};
---------
void main( void )
{
  char n;
  char *ptc;
  int m;
  int *pti;

  pti = &sdata.iii;
//ptc = &sdata.iii;  //[E1131]
  ptc = &sdata.ccc;
  m = *pti;          //m=0x7FFF
  n = *ptc;          //n=7F
}
---------
[E1131]:Error [1131] type mismatch in assignment
取得したいアドレスに入っているデータの中身の型と、定義したポインタの型が合っていない。上の例は char 型のポインタに構造体の int データのアドレスを代入しようとしたのでエラーになる。
typedef で自前の型を宣言して使うと、この「型の不一致」の言うところが、「指し示すものの型が違う」ことであることがよくわかる。構造体そのもののアドレスを取得したければ、ポインタを「その型」で定義すればいい。次のように、
---------
typedef struct{ char ccc; int iii;}test_t;
test_t sdata={0x7F,0x7FFF,};
---------
void main( void )
{
  test_t *pt;

  pt = &sdata;
}
---------
-----------------------------
おまけ
-----------------------------
rom char *dummy="string";
---------
void main( void )
  rom char *pr;

  pr = dummy;
}
---------
相手が文字列だと & が要らない。dummy は、元々がポインタ("string"の先頭アドレス)だから。
そしてもらう側は中身が文字列だから char でいいという事になる。

2010.02 - 2013.07.05

2013年7月3日水曜日

PIC C18 メモ( Warning 2066 )

-----------------------------
ROM から RAM へ文字列のコピーで、Warning 2066
-----------------------------
固定文字列をROMから読み出して加工したい場合、一旦RAMへコピーする。これに使う関数 strcpypgm2ram で、ハマった。自前の関数では出てこないのに標準ライブラリの関数を使うとワーニングが出る。
strcpypgm2ram(dest, src );
とすると、Warning [2066] type qualifier mismatch in assignment
とワーニングのメッセージ。解決策は、
strcpypgm2ram(dest, (const far rom char *) src );
と明示的に型を指定するとよい。
-----------------------------
メモリモデル
-----------------------------
ライブラリのプロトタイプには
char * strcpypgm2ram( char *dest, const rom char *src );
とある。
  • char *dest RAMを指す
  • char *src はROMのアドレスを要求しているが
  • ここにメモリモデルの情報( far 修飾子 )を加えてやる必要がある(説明書には無く、Q&Aにあった)
  • memcpypgm2ram 関数等も同じ
-----------------------------
リテラルで書いた場合
-----------------------------
strcpypgm2ram( dest,( const far rom char *)"MONDAY");
strcpypgm2ram( dest,( far rom char *)"MONDAY");
strcpypgm2ram( dest,( const rom char *)"MONDAY"); //[W2066]
strcpypgm2ram( dest,( rom char * )"MONDAY");      //[W2066]
strcpypgm2ram( dest,"MONDAY");                    //[W2066]


[W2066]:Warning [2066] ポインタの型が合わないので警告が出る(正常に動かない)

2010.02 - 2013.07.05

PIC C18 メモ(文字列)

-----------------------------
C18 文字列まとめ
-----------------------------
  1. C18 コンパイラの動作
    • 文字列定数はすべてプログラムメモリ(高位のアドレス)へまとめて配置される
    • 最適化の段階で、重複する同じ文字列定数はまとめてしまう
      (プログラム中に直接書かれたリテラルの文字列も含めて)
  2. 単独の文字列の場合
    • リテラルでも構わないが、再利用できない
    • リテラルの場合、プログラム上は見やすい
    • 同じ文字列が複数でてきても最適化されるが、修正もれが出たりしないか
    • リテラルは、エラーメッセージ表示程度にとどめたほうがよさそう
  3. 文字列の集合(*rom 修飾子)
    • 関係性のある文字列の集合体は、 rom char *rom ARY[] とすべき
    • ポインタ配列分のプログラムメモリは消費するが、後の扱いがすっきりしてくるし、それぞれの文字数が異なっても構わない
  4. 関数内に文字列
    • 関数内に文字列データをおくと関数の中でリロードされるためプログラムステップが増える
    • 文字列データは関数の外のグローバル領域におくと、初期化のときにロードされる
  5. 配列風か、ポインタ風か
    • 文字列を扱う下位の関数は、NULL終端されていることを前提とした式になっているのでポインタ渡しが多用される
    • その為、文字列定数の定義は、*pointerで表現したほうがいい
    • 文字数制限のある場合や、文字列を扱うバッファは、明確な[ N ]の配列表現を使う
-----------------------------
C18の宣言と初期化
-----------------------------
  1. 宣言
  2. char str[4];       //RAMに4バイトの領域が確保される
    char *str;         //RAMにポインタの領域が確保され、指す先はRAM領域
    rom char *str;     //RAMにポインタの領域が確保され、指す先はROM領域
    char *rom str;     //ROMにポインタの領域が確保され、指す先はRAM領域
    rom char *rom str; //ROMにポインタの領域が確保され、指す先はROM領域

  3. 宣言と初期化
  4. char *str="name";         //[W2066]
    rom char *str="name";
    char *rom str="name";     //[W2066]
    rom char *rom str="name";


    [W2066]:Warning [2066] type qualifier mismatch in assignment
    文字列はROMデータなので、ROMを指すポインタでないと警告が出る(正常に動かない)

  5. 配列の場合の初期化
  6. char str[]="name";          //[*1]
    rom char str[]="name";      //[*2]
    char *rom str[]="name";     //[W2066]
    rom char *rom str[]="name"; //[*3]


    [*1]:RAM領域に"name"の文字配列が配置される
    [*2]:Watch ウィンドウで str に"name"の表示がでるが、[*3]は、"name"の保存先アドレスが表示される

  7. 文字列の配列
  8. rom char *ary[]={"abc","def","ghi",};     //[*4]
    rom char *rom ary[]={"abc","def","ghi",}; //[*5]


    [*4]:RAM領域に、各文字列のアドレスが配置(ポインタの配列ができる)
    [*5]:ROM領域に、各文字列のアドレスが配置(ポインタの配列ができる)

  9. リテラル
  10. strcpypgm2ram( buff4,"MON");

    リテラルで書いたら、C18が勝手に名前をつけて処理する

2010.02 - 2013.07.05

PIC C18 メモ(構造体に文字列)

------------------------------
構造体へ文字列を入れ込む
------------------------------
const rom struct { char aaa; rom char *rom str; }xxx;  //strは、ポインタ
const rom struct { char aaa; char ary[5]; }yyy;    //aryは、配列
  • 構造体を構成するときも、書き方は同じで、間に{ ; } が入るだけ
  • 配列とした場合、文字列なのかどうかは関係ない
  • この構造体を配列にしたい時、ary[5]のように文字数制限したくない場合が出てくる
    その為、*rom で文字列のポインタを置くと、文字列の長さを気にしなくてもよくなる(効率は落ちる)
------------------------------
宣言と同時に初期化
------------------------------
const rom struct{ char aaa; rom char *rom str; }xxx = { 0x3f, "namex", };
const rom struct{ char aaa; char ary[]; }yyy = { 0x3f, "namey", };
  • yyy の場合、ROM領域に、aaa のデータに続いて文字列が割り当てられている
  • xxx の場合は、aaa のデータの次は "namey" の文字列のポインタ(アドレス)が入っている
-----------------------------
構造体の配列
-----------------------------
typedef const rom struct { char aaa; rom char *rom str;}my_t;

my_t rrr = { 0x3f, "namer", };
my_t sss[ 2 ] = {{ 0x3f, "names",},{ 0x4f, "longnames",},};
  • 上で typedef で宣言した構造体を、下では配列として宣言し初期化
  • 文字列は別の場所に配置され、同じ文字列が見つかったら、ひとつにまとめてくれる
  • 文字列の長さは気にしなくていい

追試(宣言と同時に初期化)、次の記述でも同じ結果だった。

const rom struct{ char aaa; rom char *str; }xxx = { 0x3f, "namex", };
const rom struct{ char aaa; char rom *str; }yyy = { 0x3f, "namey", };


2010.02 - 2013.07.05

PIC C18 メモ(初歩)

【デバッグ用のコードを実行する】
MP-LAB メニューから、Build Configuration -> Release or Debug

#ifdef __DEBUG
 //デバッグ用のコードを書く
#endif

  • Dubug を選択した場合、__DEBUG のコードが有効
  • Release にして Build すると __DEBUG のコードが無効

【 char 】
  • 符号なしで使用する場合は、-k, コマンドラインオプション
  • または、Project -> Build Option -> Project -> C18 にチェックボックス有
【 #define 】
  • 大文字で定義する
【 static 】
  • 関数に付けると別のファイルからは見えない
  • また同じファイル内に、呼び出してくれる関数がないと警告が出る
  • 変数に付けると領域が確保される(RAMアドレス固定化)
  • 関数内で定義した変数は、スタートアップルーチンで初期化が実行され、関数を呼び出す度に初期化されるわけではない
static char Data;
void myfunc_x( void )
{
    static char Data;
    Data+=1;
}
void myfunc_y( void )
{
    static char Data;
    Data+=1;
}
  • 上の三つの変数 Data はそれぞれ独立して使用できるが、混乱するので避けるべき
【 const 】
void Function ( const char data ){
    data = 123;     //NG

}
  • 引数に const の追加で、関数内で data を書き換えないことを明示
  • 従って上の場合、data に値を代入しようとするとコンパイルエラーになる。
【 GOTO 】
  • switch 文は元々GOTO
  • GOTO 文は SWITCH 文になじみやすく、分岐処理後に共通の処理が入るように場合はGOTOで飛ばした方がすっきりする
【 #include"my.h" 】
  • 自作のヘッダファイルは""でくくる
【 X+= 1 】
  • X=X+1 は X+= 1 としたほうが効率的なコンパイル結果に
【 ループ処理 】
  • 不定なループ回数に対応するには while
  • ループ回数が分っている場合は for 文が適している
  • 下の二つは同じことをしている(LCDに一文字ずつ出力)
    コンパイル後のプログラムメモリ使用量も同じだった
    この場合は、whileが直感的に分りやすく終了条件が明確
--------------
void LCD_Str( const rom char *str )
{
 while ( *str ) {
  LCD_WR( *str );
  str++;
  n++;
 }
}
--------------
void LCD_Str2( const rom char *str )
{
 for(n=0; *str!=0; n++,str++ ){
  LCD_WR( *str );
 }
}

【 文字列の最後はNULL(0x00) 】
  • char string[ 7 ];
    としただけでは最後に NULL は入っていない
  • char string[ 7 ]="x";
    と初期化しておけば二文字目には NULL が入っている
  • 文字列を For 文で操作する場合は、文字列の長さを調べてからに
  • while 文のように NULL を検出して終わる方法が好ましい
【 コンパイラのワーニング 】
  • ワーニングはコンパイルは成功するが、実機では必ずバグっているので無視しない事
2006.09 - 2013.07.04

2013年7月2日火曜日

PIC C18 メモ(ファイル分割してみた)

-----------------------------
ずらずらと書いてあったプログラムを分割してみた
-----------------------------

段々と規模が大きくなった時、見やすくするためにCファイルを分け、必要なところでインクルードして使っていたプログラムを教科書通りの「ファイル分割」してみた。

上位層をコメントアウトしておき、少しずつ開きながらつぶしていく手順だ。ソースの中身は触らないで関数のファイル間移動、宣言や定義の部分の修正のみの、ほぼ力仕事。勉強しながらの二日がかりだったが、考えていたよりも易かった。多分、元のファイルがうまく分割できていて、相互依存が薄かったためではなかろうか。

コンパイルとリンク後のオブジェクトサイズはほぼ同じになったが、MAPや、HEXファイルを比較すると関数や、変数のレイアウトが大きく違った
  • 開始前:ROM:11248, RAM426
  • 終了後:ROM:11247, RAM427
-----------------------------
>出てきた問題などの整理
-----------------------------
Q1. 構造体のデータを別のモジュールでも使いたい
  • 別のモジュール側のヘッダで extern で宣言して使う
  • ヘッダで typedef で型宣言して構造体を作る
  • 初期化や、変数の定義は、ソースで行うこと
Q2. rom char を別のモジュールでも使いたい
  • extern 通常のデータと同様に宣言
Q3. RAMデータをヘッダで定義すると二重定義になってしまう
  • RAM変数はソースの方で定義する
  • ソースがヘッダを読み込んで循環するため
Q4. ビット指定
  • 標準のdef.hがあるのでそれに書かれた型を使えばいい
Q5. 二次元配列の extern 宣言のエラー
  • extern 宣言だけでなく、低位の配列数の省略は不可
    int exsample[];    //OK
    int exsample[][3]; //OK
    int exsample[2][]; //NG
    int exsample[][];  //NG
Q6. const rom char でデータを定義してあるが、#defineでよくないか
  • LCDカーソル位置などの値を、ただし混乱を招くかも
Q7. 始めから二次元配列の構造体より、typedef した構造体をさらに配列定義した方が
  • Q1であるように、一次元の構造体で typedef しておけば、 一時的なバッファを作りたいときなど同じ宣言をする必要がない
Q8. extern 宣言したのに未定義のエラー
  • 宣言されたヘッダーファイルを読み込んでいないミス
  • ヘッダーファイルの読み込みの順番が影響する
-----------------------------
まとめ
-----------------------------
まとめは、「PIC C18 メモ(ファイル分割/hファイル)」に。
2010.02 - 2013.07.03