ラベル C++ の投稿を表示しています。 すべての投稿を表示
ラベル C++ の投稿を表示しています。 すべての投稿を表示

2012年7月26日木曜日

Cで度分形式を度に変換する

GPSから出力されるNMEAの度分形式を度に変換するコードです



  1. #include <stdio.h>
  2. void dm2d(const char *str, char *data) {
  3.     int cCnt = 0, i = 0;
  4.     int d = 0, m = 0, cm = 0, cmc = 0;
  5.     while(*(str + cCnt) != '.') { cCnt++; }
  6.     while(i < (cCnt - 2)) { d = (d * 10) + (*(str + i++) - '0'); }
  7.     while(i < (cCnt)) { m = (m * 10) + (*(str + i++) - '0'); }
  8.     i++;
  9.     while(*(str + i) != '\0')
  10.     { cm = (cm * 10) + (*(str + i++) - '0'); cmc++; }
  11.     sprintf(data, "%3d.%0*d", d, cmc + 1, (((m * 10000) + cm) * 10) / 60);
  12. }
  13. int main(void) {
  14.     char str[20];
  15.     char data[20];
  16.     scanf("%s", str);
  17.     dm2d(str, data);
  18.     printf("%s\n", data);
  19.     return(0);
  20. }

浮動小数点数を使っていないので標準的なprintf等を使わなくても簡単に出力できます

桁数が少ない等の問題はありますが
とりあえず正常に動いてるっぽいので置いておきます
気が向いたら度から度分秒に変換したり度分から度分秒に変換するコードも書くかも

2012年4月20日金曜日

STBeeMiniでタイマ割り込み+Lチカ

とりあえず
汚いですが ソースコードです
全体で100行超えちゃうのでもっと読むからどうぞ


2012年3月16日金曜日

HI-TECHでprintfとかscanf

PICでデバッグしたい時にいちいち定数を書き換えてコンパイルしなおして焼き直すというのは面倒ですよね
毎回上手に焼けましたぁってなるまで待つのもめんどくさいし

ってことで PICでscanfが使えれば便利

どうやって使うか
シリアルポートの場合
char getche(void) { while(!RCIF); return RCREG; }

void putch(unsigned char ch) { while(!TRMT); TXREG = ch; }
の2行を追加すればおk
STDOUTはputch
STDINはgetcheです
出力がchだからといって入力もchとは限らない
紛らわしい...

で 実際に使うには

普通にC言語のコンソールアプリと同じです
scanf("%d", &num);
でnumに整数を読み込んで
printf("%d\r\n", num);
でnumを文字列にして書きだす

ただまぁscanfとかの問題もそのまま残ってるはずなので
製品には使用しないほうがいいよなぁ
もっとも使う用途も思いつかないけど

↑のget/putはとにかく簡単に済ます方法なので
速度とかを優先するならこのコードではダメです

例えば9600bpsで通信した場合1バイト送/受信するのに1mS位かかります
これはかなりのロス
バッファとか割り込みとかを活用すれば
100バイトでもメインのコードは1mSかからずに処理できるので
ただそうすると割り込みやら色々めんどくさいんだよなぁ


とりあえず PICで数値とか読み取る方法
覚書でした

2012年3月11日日曜日

1でdisなフラグの謎

PIC18F2550でRB0を入力として使ってた時にハマったおかしな点

ADON = 0; ADCON1 = 0;
としていて デジタル出力として正常に使えていたのでそのまま使っていた
そして入力として使おうとした時 なぜか常に0になる
んで データシート見て気がついた
ADCON1 = 0;ってアナログ入力じゃん

ええ 初期設定がアナログだから0にしてアナログをオミットかと思ったら
デジタルを有効にするために1を設定
なのね
PICって時々「1で無効にする」ってのがあるよね...
多分これ前も引っかかったよなぁ…

ということで PIC18F2550ですべてデジタルポートとして使う場合
ADCONbits.ADON = 0; ADCON1 = 0x0F;
というのが正解
ADCON1の下位4bitが0でアナログ 1でデジタルになる
上位2bitは無効 上位3-4がVREFで これは0で
なのでADCON1 = 0x0F;
詳しくは データシート265ページあたりの
"10-BIT ANALOG-TO-DIGITAL CONVERTER (A/D) MODULE"
を参照してくだされ

16Fのアナログ入力に慣れてると
18Fのアナログ入力は意外と厄介なので
使う場合はまずデータシートを見てください
16のように「好きなポートをアナログに」っていう設定ができないので

では
PICで前にもやらかしたバグを再発させたという恥さらし覚書でした

2012年3月1日木曜日

Cでbmp画像

ビットマップファイル
比較的簡単な構造のファイルフォーマット
ってことで実装してみました

簡単な解説ページ2ヶ所くらい見れば理解できると思います
バイナリエディタ必要ですけど…

で まだコードが酷いので結果だけ


Cで青と緑のグラデーション
赤は0固定です
とりあえずforのネストで

さて ちゃんとコード整理して まとめて 扱いやすいように改造しなければ

2012年2月26日日曜日

HI-TECHで再帰

どうやらHI-TECHでは再帰ができないらしい
まぁ組み込み用コンパイラならそれが妥当か

しかし
printf関数で%sはポインタ渡してなすがまま
という仕様で作ってたから
作りなおしだーよ

それにポインタを関数で渡しちゃったら
char text[] = "hello %d";
みたいなのが渡された時に困るからな
今回はforで\0まで出力

んで 現状10進での出力しか対応してないけど
やっぱ組み込みで使うなら2進も必要だよなぁ

ちなみにオリジナルのprintf関数で九九を出力させるコードは
ROM1399/RAM119くらい消費してる
結構重いよなぁ

そして いろいろいじってて気がついたのだけど
htc.hをインクルードするとstdio.hもインクルードされるのね
めんどくせぇよorz

つーことで
自分でprintf作っちゃうよぉ というお話でした
うーん 他に色々やることあるのに
優先順位低い方ばっかりやってるなぁ

2012年2月23日木曜日

10進文字列を整数に

ちょっと実験コードで文字列を整数に変換する必要があったので
便利な関数が用意されてるような気もしたのだけど
探すのもめんどくさいので作ってみました

int S2I(const char *str) {
    int i = 0;
    for ( ; *str != '\0'; i = (i * 10) +  (*str++ - '0'));
    return i;
}
こんな感じ
引数で文字列を受け取って
forで\0になるまでiに加算していく

とりあえずデバッグで使いたかっただけなので
手っ取り早く実装してしまいましたが
ちゃんと使いたいならまじめに実装すべき

↑のコードだと
例えば
char data[] = "12345";
int i;
i = S2I(data);
はi = 12345;と同じ動作ですが
char data[] = "12345\n";
int i;
i = S2I(data);
の場合はi == 123412となります

あと10進数限定
16進とか渡しちゃダメです

とりあえずCでデバッグに使いたいとかなら便利かもしれない
C#? int.Parseで良いじゃん(´・ω・`)

C#は色々と便利なんですけどね
C#使うとCが使えなくなっちゃうorz

2012年2月14日火曜日

B言語

懐かしきB言語です
使った事無いですけど

さて
某コンパイラのヘッダ見てたらこんなのがありました

int putc (auto char c, auto FILE *f);

auto?
なにそれ 美味しい物?
んで 調べてみると
どうやらB言語の名残でC言語では見ることはないらしい
(組み込みCコンパイラのstdio.hにふっつーに書いてあったけどな!)

んで B言語を調べてて 面白そうなコードを発見
Wikipediaに書いてあるんだけど与えた数値を与えた進数で表示するコード

んで C言語に移植したコード


Wikipediaのコードは最大10進数まで(※)なので
改造して36進数まで対応させました
36以上は数字もアルファベットも使い切るので
これ以上は実用性なくなるなぁ
256進数なんて誰も使わねぇよ...

んで 実行するとこうなる

左から 10進/16進/36進
もうちょっと機能を足したら
プログラムの動き方の説明とかで使えるかな?

ただこのコード
PICとかで使うのはやめたほうがいいと思います
PICのプログラムカウンタは通常の場合8レベルでスタックされるので
関数が8回くらい以上呼び出されるとマズイです
int型の場合は最大で10回くらい呼び出される可能性があるので
それよりも少ない数 たとえば120で3回としても
それ以前に複数回呼び出されていないとも限りません
なので PICでこのコードはマズイと思います
(ここらへんの動作は試していないのでテキトーに書いてるだけですけども)
まぁわざわざPICでこういうコードを使う必要もないと思いますが…


とりあえず 暇つぶしにB言語をC言語に移植(コラ)してみましたというお話

B言語ってぐぐってもC言語しか出てこないんだよなぁ




※)/* 以下 読み飛ばしても大丈夫な感じの記述 */
 アスキーコードで数字は'0'(0x30)から続いてることを利用した処理
 結構な頻度で使われる
 ただし9からAは連続していないから
 元のコードでは9までを使う10進しか対応していない
 そこでifを使って10以上は+=7で7個の記号を飛ばしてAにしている
 小文字を使いたい場合は+=39とすればいい
 んで今回はZ以降の処理をしていないので37進数を使うと36に'['が使われる
 そして42に'a'が来る
 ASCIIでは71(~)まで対応していてそれ以上はおかしな表示になる
 (半角カタカナでも表示されるかと思ったらされなかった)

2012年2月10日金曜日

printf関数 とか

組み込みマイコンだと stdio.hが提供されていない場合があります
もう分かるよな?

つまり PICでprintf関数を使いたくても普通は使えません
と言うか使うな ということなんだけど
やっぱ使いたいじゃないですか
ということで
PCで試して見ました
printf関数って作れるの?って

まぁとても人様に見せられるようなコードじゃないので
まだ隠してますが
結果から言えば
「限りなく可能に近い可能」
えっとですね 一応「最低限必要な機能」は実装して見ました
まぁ元のprintfだって人間が作ってるんだから作れないわけ無いよなぁ


組み込みで最低限printfに要求されるであろう数値出力もひと通りカバーしてます
%+015d みたいな感じのフォーマット指定
まぁそれくらいしか対応してないんですけどね

ちなみに九九を出力する実行コードは14kbくらいです
おっも…
何も考えずに普通に九九を出力するコードを書いたら実行コードは9kbくらいです

と思ってVisual C++ 2010で試したら29kbって…
Winって凄い肥大化してますよねぇ…
 MacはHDDが比較的小容量でも大丈夫なのは全体がコンパクトだからだよなぁ
 起動が早いのもコンパクトっていうのが大きいんだろうなぁ



でも さすがにこのまま組み込み機器に移植するのは気が引けます
ちゃんと動くかどうか と言うより コードが汚い
あとメモリ無尽蔵に使ってる

多少コードが複雑になってもいいならメモリ使用量は大幅に減らすことができるし
でもそうするとマイコンに持っていったときに遅くなるし
でも現状のままだとメモリが足りないし
うーん どうするか
ま 一番手っ取り早いのはメモリ容量の大きいマイコンを使うことですよね
28ピン(300)でもメモリが1.5kとか2kとかもありますし
(周辺機能に問題があったりするけど)



とりあえず printf関数を自作することはできるの? という問には答えが出たと思います
「全機能を実装するんじゃなけりゃ作ることはできる」

そーいえば数値を文字列化する関数とかも自分で作ってますよ
stdioが無いのにそんな便利な関数が用意されてるわけ無いですからね
結構勉強になりますよ
実際役に立つかはわかりませんけど
勉強なんてそんなもんです

ま 面白いので良しとします
少なくとも“デバッグ環境がない状態で原因も条件もわからないバグを抜く“みたいな作業よりは楽しいですよ
あー あの不具合もどうなったんですかねぇ(白目

2012年1月3日火曜日

組み込みに最適なプログラミング言語

プログラミング言語は様々な種類があります
どっかには「1日に1個生まれては消えていく」とか書いてあった気がするのだけど
まーそんなにあっても使い切れませんわな
覚えるのだけでも大変(大体はCみたいなある程度普及しているものを基礎にしているのだけど)

つーことで
組み込みに最適なプログラミング言語はなんぞや というお話

まず最初に浮かぶのはアセンブラ
PICならアセンブラで決定です ぼくは使わないけど

ただ 大規模になってくるとアセンブラでは到底不可能です
(トライステートの電波時計とか アセンブラで書いてます 凄いですよね)

僕も最初はアセンブラでしたが今は書けと言われても無理です

アセンブラは書いたとおりに実行してくれる(場合が多い)ので
実行速度とかでは有利です
ただ  やっぱめんどくさいですよね

ということで Cの登場です
液晶でもシリアルでも 1回ヘッダとかでまとめちゃえば
次はインクルードするだけで面倒な処理をしなくても実行してくれるという利点があります
標準ライブラリとかもありますし

ですが 「Cが便利」と言っても
PCのプログラムでCを使ってシリアル接続とか解説してるページ無いですよね?
マイコンで完結してしまうものしか作れないという欠点があります

ということで
C#の登場です
C#は便利です GUIもCUIも作れます
もちろん組み込みにも使えます(遅いけど)
シリアル接続も標準で使えます
液晶はライブラリをダウンロードして下さい

C#はCに似ているので C#を覚えてからCを使うというのもアリです
(配列とか色々と違ってくるので応用能力は必要ですが)


とりあえず
「手っ取り早く組み込みマイコンを使ってみたい」
というなら Netduino PlusでC# という組み合わせがおすすめです
マイコンでSDカードにテキストファイルを書き込むとかも簡単にできるようになっているので

2011年12月31日土曜日

大きな桁数の足し算

最近生活リズムが乱れまくってますね…

ニートなんてこんなもんです(キリッ

ごめんなさい...


とりあえず 大きな数値同士の足し算をする関数
第一引数に最初の数値を
第二引数に次の数値を
第三引数に結果を
戻り値に結果の桁数を
という感じ
ムダな処理とかもかなりあるけど
PCで動作確認だけする予定だったので
移植性とか全く考えてないんですよ…
だってPICで1000桁同士の足し算とかやらないじゃないですか…

とりあえず「結果の桁数がintに収まる程度」までは計算できると思います
(実際問題として配列とかの最大数の問題があるので intの最大数までの計算はできませんが)

引数はすべて文字列へのポインタです
最低限の条件として
第一引数長 < 第三引数長 && 第二引数長 < 第三引数長
である必要があります

処理としては
文字列長獲得

文字列前後反転

文字列から数字を抜き出して足し算

文字列前後反転
という感じです

戻り値がintなので
例えば文字列の中に数字以外が含まれていたらEOFを返して終了
とかにしてもいいかも

このコードはけっこームダが多い気がするので
実際には使わないが吉と思いますけど
一応書いておきます
デカイ数字を扱うのにこういう方法もあるんだよ~
という一例として


int add(char *text1, char *text2, char *text3) {

char
*text1p, *text1q, *text1c,
*text2p, *text2q, *text2c,
*text3p, *text3q,
ca, cal = 0;
int i;

for ( text1p = text1; *text1p != '\0'; text1p++ ) ;
for ( text2p = text2; *text2p != '\0'; text2p++ ) ;

text1c = text1p;
text2c = text2p;

*(text1p--) = '\0';
*(text2p--) = '\0';

for ( text1q = text1; text1p > text1q; ) {
ca = *text1p;
*text1p = *text1q;
*text1q = ca;
text1p--;
text1q++;
}

for ( text2q = text2; text2p > text2q; ) {
ca = *text2p;
*text2p = *text2q;
*text2q = ca;
text2p--;
text2q++;
}

for ( i = 0; ; i++ ) {
if ((text1 + i) >= text1c && (text2 + i) >= text2c && !cal) {
*(text3 + i) = '\0';
break;
}
*(text3 + i) = (
((text1 + i < text1c) ? (*(text1 + i)) - '0' : 0) +
((text2 + i < text2c) ? (*(text2 + i)) - '0' : 0) +
cal);
if (*(text3 + i) >= 10) {
*(text3 + i) -= 10;
cal = 1;
} else {
cal = 0;
}
*(text3 + i) += '0';
}
text3p = text3 + i - 1;
text3q = text3;

while (text3p > text3q) {
ca = *text3p;
*text3p = *text3q;
*text3q = ca;
text3p--;
text3q++;
}

return i;
}


とりあえず
この程度の桁数なら「数世代前」のPCでも一瞬で計算できます
まぁ… あたりまえっちゃー当たり前なんですが
PCはC2Q RAM8G Win7Ux64

※画像はCで作った「コマンドライン引数で与えられた数値を足してstdiutに出力する」というコンソールアプリをC#で呼び出してGUIにしているものです


お世辞にも綺麗と言えるコードではないですけど
AOJでトップランククラスのコードだとどういう風に扱ってるんでしょ
まずテキストで扱うということはしてないと思うのですけども…


↑のコードは上記PCでVisual C++ 2010を使って作成したものです
またGUI部分はVisual C# 2010で作成しました



/* 今年最後の1日は「彼女はつっこまれるのが好き!5」を読みながら開始しました
  どんな一日になるんだろう
  とりあえず宇宙へのパスポートを読みながら寝ようかと思いつつ */

2011年12月30日金曜日

ビット長と最大数の関係


みずらいけど 各ビット長での符号あり/なしの最小値と最大値の表

Excelの内部計算がIEEE 754だから 36bitくらいまでしかちゃんと表現できない
どうやったらちゃんと表現できるだろうね
一番手っ取り早いのは頭で倍々して行ってメモ帳とかで打ち込むのかw

とりあえず計算方法

符号あり最小値:((2^(ビット長 - 1)) * -1)
符号あり最大値:((2^(ビット長 - 1))-1)
符号なし最小値:0
符号なし最大値:((2^(ビット長))-1)
という感じ

符号ありは1bitが符号に割り当てられるので使えるビット長-1の長さが実際に使える範囲です
符号なしは2進のn乗を計算して1減算するだけです



#ifdef nonsns

掛け算とか割り算って 足し算とか引き算を繰り返すだけだから
数百桁の足し算とか引き算をできる関数を作ってやれば
128bitの最大値とかも計算できるだろうけど
足し算引き算掛け算割り算をする関数を作った所で
使い道が無いのが欠点だよね
超大規模な予算とか
天文学的数字の計算にしか使えない

Cで書いて
アセンブラに移植して
「PIC アセンブラでも1.8446744e+019位余裕で扱えます!」
とか? いらねぇよ…

#endif

とりあえずIEEE 754 と言うか 浮動小数点数とかが まだよくわかってないから
そこら辺も勉強し直さねば

Amazonから本とかいろいろ来て積み本もどんどん増えてるし...

とりあえず


プログラミングの何がめんどくさいって扱える数字がめんどくさい
この表だと0bitが左だからなんか見づらい

多分作りなおす


さて
「128bitCPU」とか
ぐぐればいろんな記事が出てくるけど
個人的には128bitCPUなんてどうでもいいからlong long longみたいな変数に対応して欲しい
long longがlongの倍ならlong long longで128bit変数
みたいな

charは環境によって違うみたいね
HI-TECHだと「普通はunsignedだけどコンパイラオプションでsignedだよ」みたいなこと書いてあるから
可能であれば明確的に指定すべきかも
他の人にコードを渡す場合とか
コンパイラを変更した時とか 移植したときに
問題が出るかもしれないし

いちいちsignedを指定するのはめんどくさいけど
保守性とかから見れば 多少は
有効かと

組み込み用の石だと符号ありと符号なしでは実効速度が違ったりとかする場合があるから
そういう意味では組み込み開発者はちゃんと区別するべきなのかも

PIC16F88で4096個のパルスを出力するコードとか 大変だったなぁw
(時間制限がキツイからfor(int i = 0; i < 4096; i++) { }とかじゃ遅すぎる)
結局charのネストでも重くて
結構無茶なコード書いた気がする

TLC5940を制御するだけなのだけどね…
Arduinoだとライブラリがあるけど
PICは探して出てくるほど多くなかったから
気が向いたら修正して公開するかも

2011年10月25日火曜日

PICで高速にシリアル通信

PICでpurintfを使う場合
普通は

void sputch(const char ch) {
while(!TXSTAbits.TRMT);
TXREG = ch;
}
っていう関数を使うことになる
これはシリアルで文字列を送信するためのものなんだけど
9600bpsだと1bitあたり
1/9600*1000000で104uSかかる
それが8bit+2(スタートストップ)で
1バイト送信するのに1mSくらい
ということは30文字送信するのに30mSかかる
これは無視できない時間

ということで
割り込みで送信できるようにしてみた


char text[50];
char textSize;
char textCnt = 0;



static void interrupt intr(void) {
if (TXIF) {
if (textSize > textCnt) {
TXREG = text[textCnt];
textCnt++;
} else {
textCnt = 0;
TXIE = 0;
}
}
}

これで


void main(void) {
init();

textSize = sprintf(text,"hello world ... serial TEST\r\n");
TXIE = 1;

for ( ; ; ) {
}
}
とかだと 300uS程度で処理が完了する
あとは割り込みで勝手に送信してくれる


ただ 当たり前だけどこれはハードウェアシリアルが有るPICに限られます


2011年9月21日水曜日

とりあえずまぁ

学食でわかめごはんS頼んだのになぜかMが出てきて
そのまま食ったけど寝不足で例のごとく吐き気が出るほどに眠くて
眠気覚ましにとか言って1000桁同士の足し算とかを
実装しちゃうくらいにはCに慣れてきた
まぁROM/RAM/時間制限無しだけどねw
一応2000桁同士でも大丈夫なはずなんだけどMacの文字入力制限があるっぽい

あっ 一応無事帰宅しました^^
(帰ったら即寝そうだから車の中でかいてるのはヒミツ)

AOJのアカウントなんてとっくに忘れてるし(蹴
まーそのまま通るわけもないから
誰か暇な人試してみてよーとか思いつつ

あと引き算実装すれば
掛け算とわり算が作れないこともないから
いろいろ遊べる と思う

あー 液晶モジュール…

2011年9月20日火曜日

【追記】LCDモジュールに外字登録

追記
 このコードとんでもないバグを抱えてるらしい
 壊れはしないと思うけど なんか怪しい
 ということで↓のコードは使用禁止
 できれば今日中に修正コード書きます
 が 明日朝早いので無理だったら来週まで更新できませんorz
 (水曜日~土曜の4日連続で早朝出発のイベントが...)
******************************************************

マイコンで制御できる液晶モジュール(*)に外字登録を試してみたよ

今回作った関数はこれ↓
/* *** */

void LCD_Cset(char addr, char ca[]) {
LCD_8set(0x40 + addr, 0);
putch(ca[0]);
putch(ca[1]);
putch(ca[2]);
putch(ca[3]);
putch(ca[4]);
putch(ca[5]);
putch(ca[6]);
putch(ca[7]);

LCD_clear();
}

/* *** */
使い方は
/* *** */

const char data[] = {
0b00000000,
0b00000100,
0b00000100,
0b00011111,
0b00000100,
0b00000100,
0b00011111,
0b00000000 };
LCD_Cset(0, data);

/* *** */
みたいな感じで
でも配列を使うと結構コストが高いから
配列を使わないようにしてもいいかも

putchはHI-TECHでprintf関数を使えるようにするためのもの
簡単に言うとRSを1にして1byte送信

表示する場合はaddrで指定したアドレスをRSを1にして送信すればいいです

外字登録って結構便利で
ゲームを作ってる人とかカレンダーを作ってる人もいるみたい
あとおっきな文字を表示することもできるらしい

注意点としては外字は8文字までしか登録できないみたい
8文字を超えて登録しようとした時の処理とかは書いてないから各自注意してください


ということで 覚えておくとちょっと便利な小ネタでした

*これとかこれで動作確認をしたよ

2011年9月15日木曜日

DIPスイッチを使うよ

今回はDIPスイッチの基本的な使い方を解説するよ



回路は↓これ
どっか間違ってる気もするので参考程度に
DIPSWの左側がプルアップ抵抗 今回は470Ωを使ったけど本来はV+に直結するべき
DIPSWの右側はプルダウン抵抗 4.7kΩを使ったよ
基本的にこれだけで使える
スイッチがOFFだとPICはLになりスイッチがONだとPICはHになる
一般的にはプルアップしてスイッチを押すとLになるけど
今回はわかりやすいように(+後々簡単なように)アクティブHにした

そしてコードは↓これ
/* *** */

#include <htc.h>

__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & BOREN_OFF & LVP_OFF & DEBUG_OFF);

#define _XTAL_FREQ 20000000
#define TXP RC6

void send(char);

void main(void) {
TRISC = 0x0F;
PORTC = 0;
TXP = 1;

char data = 0;

while(1) {
if (data != (PORTC & 0x0F)) {
send(0x08);
data = (PORTC & 0x0F);
send(data + (data > 9 ? (0x37) : ('0')));
}
__delay_ms(5);
}
}

void send(char ca) {
TXP = 0; __delay_us(100);
TXP = ((ca >> 0) & 1); __delay_us(100);
TXP = ((ca >> 1) & 1); __delay_us(100);
TXP = ((ca >> 2) & 1); __delay_us(100);
TXP = ((ca >> 3) & 1); __delay_us(100);
TXP = ((ca >> 4) & 1); __delay_us(100);
TXP = ((ca >> 5) & 1); __delay_us(100);
TXP = ((ca >> 6) & 1); __delay_us(100);
TXP = ((ca >> 7) & 1); __delay_us(100);
TXP = 1; __delay_us(100);
}
/* *** */

一応
5ms毎にスイッチを監視し変更があればASCII16進に変換してバックスペースと共に送信
という感じ
これはぼくが作ったLCDモジュールの仕様によるもので
バックスペース(0x08)→1行目1文字目に移動
ENTER(0x13)→2行目1文字目に移動
ESC(0x1B)→表示クリア
になってるから


今回はこれ以外に機能を付けなかったから通常のポート使用量だけど
気が向いたらもうちょっと少ないポート数で使えるような工夫も紹介するかも

2011年9月9日金曜日

PICでLチカ【改】

#include <htc.h>
#include <config.h>

#define _XTAL_FREQ 20000000

#define LED RB5

void main(void) {
TRISA =   0b000000;
TRISB = 0b00000000;
TRISC = 0b00000000;
PORTA = 0;
PORTB = 0;
PORTC = 0;

while(1) {
LED = !LED;
__delay_s(1);
}
}

コンフィグをヘッダにすることで移植を楽にした

自分は現状88と873Aしか使ってないから

#if defined(PIC16F88)
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & MCLRE_OFF & BOREN_OFF & LVP_OFF & DEBUG_OFF);
#endif

#if defined(PIC16F873A)
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & BOREN_OFF & LVP_OFF & DEBUG_OFF);
#endif
みたいな感じになってる

これでプロジェクトを作るときに選択した石のコンフィグが作られる
まぁ「典型的なコンフィグ」しか設定できないけどね
ちょっと動作確認したいだけならこれでも大丈夫だと思う

2011年9月5日月曜日

PICで液晶

/* *** */

#include <htc.h>

__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & MCLRE_ON & BOREN_OFF & LVP_OFF & DEBUG_OFF);

#define _XTAL_FREQ 20000000

#define E RB0
#define RS RB1
#define RW RB2

#define D4 RA3
#define D5 RA2
#define D6 RA1
#define D7 RA0

void LCD_init(void);
void LCD_clear(void);
char LCD_printf(char text[]);
void LCD_8set(unsigned char, char, char);
void LCD_4set(unsigned char, char, char);

static void interrupt intr(void) {
if (RCIF) {
RCIF = 0;
char c = RCREG;
if (c == 0x1B) { LCD_clear(); return; }
if (c == 0x08) { LCD_8set(0x80, 0, 0); return; }
if (c == 0x0D) { LCD_8set(0xC0, 0, 0); return; }
LCD_8set(c, 1, 0);
}
}

void main(void) {
TRISA = 0b000000;
TRISB = 0b00000000;
ANSEL = 0;
PORTA = 0;
PORTB = 0;

LCD_init();

LCD_printf("LCD init ok...");
__delay_s(1);
LCD_clear();

for(;;) {
LCD_printf("hello");
__delay_s(1);
LCD_clear();
__delay_ms(1);

LCD_printf("world");
__delay_s(1);
LCD_clear();
__delay_ms(1);
}
}

void LCD_init(void) {
__delay_ms(15);

LCD_4set(0x30, 0, 0);
__delay_ms(5);
LCD_4set(0x30, 0, 0);
__delay_ms(1);
LCD_4set(0x30, 0, 0);

LCD_4set(0x20, 0, 0);

LCD_4set(0x20, 0, 0);
LCD_4set(0x80, 0, 0);

LCD_4set(0x00, 0, 0);
LCD_4set(0x80, 0, 0);

LCD_4set(0x00, 0, 0);
LCD_4set(0x01, 0, 0);

__delay_ms(2);

LCD_8set(0x06, 0, 0);

LCD_8set(0x80, 0, 0);

LCD_8set(0x0C, 0, 0);
}

void LCD_clear(void) {
LCD_8set(0x01, 0, 0);
__delay_ms(1);
LCD_8set(0x0C, 0, 0);
}

char LCD_printf(char text[]) {
char c;
for (c = 0; text[c] != '\0';c++)
{ LCD_8set(text[c], 1, 0); }
return c;
}

void LCD_8set(unsigned char data, char RSb, char RWb) {
LCD_4set(((data << 0) & 0xF0), RSb, RWb);
LCD_4set(((data << 4) & 0xF0), RSb, RWb);
}

void LCD_4set(unsigned char data, char RSb, char RWb) {
RS = RSb;
RW = RWb;
D4 = ((data >> 4) & 1);
D5 = ((data >> 5) & 1);
D6 = ((data >> 6) & 1);
D7 = ((data >> 7) & 1);

__delay_us(500);
E = 1;
__delay_us(1000);
E = 0;
}
/* *** */

PIC16F88 @ 20MHz
液晶との接続はコード最初のほうで確認してください
あと__delay_s()の追加をしてない人は
__delay_msに修正するなり
#define __delay_s(sec) { __delay_ms(sec * 1000); }
とか書き足すなり(動作確認はしてないけど)
適当に対応してください


実はこれARMチャレンジャーの液晶の動作確認に作ったコード
なぜかARMからだと動作してくれなかったので
PICで動作確認
んでこのコードをARMに移植しても動かないんだよね…
LCDの電源が5VでARMが3.3Vだからとかいうくだらない理由でも無いと思うんだけど…
何なんだろう

2011年9月4日日曜日

手っ取り早くPICでデバッグするためのUSART

/* *** */

void send(unsigned char data) {
TXP = 0;
__delay_us(100);
TXP = ((data >> 0) & 1);
__delay_us(100);
TXP = ((data >> 1) & 1);
__delay_us(100);
TXP = ((data >> 2) & 1);
__delay_us(100);
TXP = ((data >> 3) & 1);
__delay_us(100);
TXP = ((data >> 4) & 1);
__delay_us(100);
TXP = ((data >> 5) & 1);
__delay_us(100);
TXP = ((data >> 6) & 1);
__delay_us(100);
TXP = ((data >> 7) & 1);
__delay_us(100);
TXP = 1;
}

/* *** */

出力するポートを予め
#define TXP RA4
みたいに定義しておく必要があります
あと__delay_usを使うためにhtc.hもインクルードする必要があります
それと↓みたいにコンパイラに動作クロックを指定する必要があります
#define _XTAL_FREQ 20000000 // 動作クロック20MHz

ということで内部クロックを使って動作周波数が可変とかだと無理です
そういう時はdelay関数自分で作ってください


とりあえずこれだけで9600bpsで一文字送信はできます