C.K PRIVATE C言語プログラム基礎,

WEB・PIC

C.K PRIVATE C言語プログラム基礎

C.K PRIVATE /  C言語プログラム基礎のページです
C言語の基礎の部分をリストアップしています

C基本

プログラム基本構成
宣言部
特性宣言
モジュール使用宣言
インクルードファイル指定
グローバル変数宣言
プロトタイピング
main関数部
main()
{
ローカル変数宣言
実行文(式、文、関数)

その他関数部
関数1
{
ローカル変数宣言
実行文(式、文、関数)
}
関数2
{
ローカル変数宣言
実行文(式、文、関数)
}

 

 

データ定義
データには「定数」と「変数」があります。定数とは、PGの実行前から値が決まっており
PG実行後も変わらない数を言います
これに対して変数は、データの入れ物であって、中身の値はPGの実行に伴って変化します

定数の定義
定数は数値などを直接しても構わないのですが、PG中で何回も使ったり、変更がありうる場合には
以下のMAXのように定数100に名前をつけて、この名前で扱うことが出来ます
#define MAX 100

 

変数の定義
変数は必ず名前をつけて使いますが、一定のルールの従えば、プログラマが自由な名前をつける
ことができます、しかし、あとからの読みやすさを考えて、名前だけで内容の意味が推定できる
ような名前付けをする必要があり、あまり短い短縮形の変数名を使うのは避けるべきです
変数には「データ型」と呼ばれる、変数の中味の形と大きさを決める定義があり、全ての
変素はいずれかの型を定義して使います、以下の例では、i,jという名前の変数をint型という
整数型で定義しています
int Counter,i,j
型が異なる変数同士で演、代入を行うと、自動的にいずれかの型に合わせられて演算されます
ので注意が必要です、例えば1バイトの型の変数に255以上の数値を代入しようとしたり
整数の型に実数の1,5のような数値を代入しようとすると、結果は期待した通りにはなりません

 

実行文
実際の命令として実行されるものとして記述するのが、「実行文」で、これには、「式」と「文」
と「関数の呼び出し」の区分があります、実際の例で説明しましょう、以下は実行文の例です
「例」
y = x + 2;
x = x + 1;
data = (x * y)/16;
value = calc(3,5) ;

①式
式というのは、数学でいう数式と似ていて書式も似通っています、しかし、根本的に異なるのは
「=」の意味です、式で使われる{=」は、数学では「等しい」という意味ですが、C言語では。
左辺へ右辺の結果を「代入する」という機能を果たしています、そのため、上記の例のような、
「X = X+1]という数学ではありえない記述も、代入としてみれば成立することになります
②文
最後がセミコロン(;)で終了している式を「文」と呼びます、文は1つ以上の式で構成されて
います、又複数の分の集まりを「ブロック」と呼び、中括弧{}で囲みます
③関数の呼び出し
関数を使うときには、「関数名(実引数);」という書式で呼び出すことが出来ます、上記の例では
「value = calc(3,5;」がこれに相当します、別のブロックで定義した、calcという関数を呼び出し
実引数の3,5を使って処理をするという意味です


関数の定義
C言語のPGでは、関数は基本の構成要素となっています
関数の基本構成は以下のようになっていて、中括弧{}で囲まれたブロックの中にその実体
を記述します、ブロックの中に、実体として記述できるのは、データ定義と実行文です

{関数の基本書式}
データ型 関数名 (仮引数)
{ (例)
データ定義;) int calc(int a,int b)
実行文;  )ブロック {
実行文;  ) int c;
} c = a + b;
return c;
}
上記の例では、calcという関数の定義をしています、int a, int bは
仮引数を呼ばれ、関数の呼び出し時に与えられた実引数を受け取ります

変数の動き
変数の宣言 ←全体に有効
main(){
変数の宣言 ←mainの中で有効
関数

関数(){
変数の宣言 ←関数内のみで有効

簡単な例 肥満度算出関数にelse if文を用いて3種類の表示をする
void GetFantness( int t, int w ) //t;身長, w:体重から,肥満を判断する関数
{
double stdw; //標準体重を計算するための変数宣言
stdw = 0,9*(t - 100); //標準体重を求める
if( w/stdw >= 1.2 ) { //体重比を計算し、1,2以上なら...
puts("Weight over,too fat!\n");//肥満を表示
printf("suitable weght is %4.1fkg\n",stdw);//標準体重を表示
} //文が2行に渡るのでここで閉じる
else if( w/stdw <=0.8 ) //そうではなくて0,8以下なら
puts( "You are too slim\n" );//痩せ過ぎを表示
else //そうでもなければ、、、
puts("You are healthy\n")//適正と表示

 

void 戻り値、引数 プロトタイプ宣言
voidとは「無」や「空っぽ」をあらわします

下記をプロトタイプ宣言といいます
void ioport (void);
戻り値 関数名  引数

時間遅延

引数 プロトタイプ宣言がvoid delaym(void);だった場合
例えばDelayMs(250);があったときに1.8秒周期に点滅などをさせたい時DelayMs(900)と命令が
書けたら、点灯して900、消えて900で、ぴったり1.8秒になるのですが
deleym(900)と書けるようにするためには、()内に数字が書けるようにしなければなりません
つまり、引数が[void]では困るのです、ので引数にデータ型を規定します
「900」のような数字を入れたいわけですから整数で、ある程度大きな数が扱える事が
条件になります、確かint型がそういう数でしたね、ですからint型にあわせて関数を
書き換えます
宣言部
void delaym(void);の(void)の部分を→にします void delaym(int);
これでdelaym();はint型の整数を扱う関数になったわけです、ただこれだけでは駄目です
処理部分もそれに合わせて型を変えなければなりません
void delaym(int);の(int)の部分を(int a);になりますつまり
void delaym(int a);となります
処理部(int a);の引数
処理部の方に入っている「a」ですが、これは数に名前をつけたのではなく、数を入れる「a」
という箱だと考えてください。
この場合メイン関数の「delaym();」は入力された数を処理するための関数なので、入力された
その数を一時的に入れる容器がないと、数を受け取ることが出来ません
その容器、便宣的に「a」という名前をつけているわけですね
ですからdelaym(900);とプログラムに記述すると処理部では自動的にa=900という
処理を行うわけです、

 

入出力ピンの使い方
入出力(i/oポート)はPICのもっとも基本的な内蔵周辺回路であり、スイッチや発光ダイオードなど
色々な物を接続してPG制御ができます、ここではその入出力ピンの制御をするPGを作る際のポイントに
ついて説明します

入出力モードの設定と入出力
PICにはi/oポートが何組か用意されています、そかもそれぞれのi/oポートのピン1本1本を独立して
入力か出力PGで決めることが出来ます、これを入出力モードの設定といいます

TRISXレジスタで入出力モード設定
入出力の設定は、TRISA,TRISBという制御レジスタで設定します、このTRISXレジスタの各ビットが
対応する各ポート(PORTXまたはLATX)の同じ位置のビットの入出力モードを決定します、入力の
時は「1」出力の時は「0」に設定します、
電源ON後やRESET後は全て「入力」となるように設定されています

基本的な実際の入出力
実際に入出力する場合は、まずPORTAとPORTEはRESET後のデフォルトではアナログ入力モードに
なっています、単純なデジタル入出力として使うときにはADCON1レジスタでデジタルモードに
設定しておく必要があります、デフォルトがアナログになっているのは、デジタル入力にすると、
ピンが開放されているとき余計な電流を消費するからです
そのあとでTRISXレジスタを設定して入出力を決定します

C18、C言語による入出力
MPLAB C18コンパイラでの入出力ピンの制御は、アセンブラ言語と同じように、レジスタへの書き込み
で行います、ヘッダファイルでレジスタ名とビット名が定義されていますから、アセンブラと全く同様に
レジスタ指定で8ビット並列に入出力する方法と、
ビット指定でピン単位で制御する方法の2通りがあります
実際の入出力の流れ
① ADCON1レジスタでポートをデジタルに設定する
② 入出力モードをTRISXレジスタで設定する
③ 出力はLATXレジスタで、入力はPORTXレジスタで行う
実際の記述の仕方としては、下記のように8ビット並列のバイト単位と、1ビット単位の
両方があります
TRISA = 0xc3: //入出力モード設定
LATA = 0x24; //8ビット並列出力
LATBits.LATA3 = 1; //1ピンのみ出力
Data = PORTA; //8ビット並列入力
SW1 = PORTA,RA0; //1ビットのみ入力

 

☆I/O high low
    例LED LEDが光るということはLEDに電圧がかけられていることをあらわします
つまり、LEDには相応の電圧がかけられているわけで、当然、対応するポートの電圧は高い状態わけです
このポートの電圧が高い状態を「high」といいます、低い状態は「LOW」といいます、PICはポートに
「1」を入力することでポートの電圧を「high」に変える事が出来るのです、同様に「0」を入力することで
「LOW」に変えることも可能なわけです

☆数値の記述方法と呼び方
形式 書き方 記述例
2進数(Binary) 0b <binary digits> 0b01101010
10進数(Decimal) <digits> 106
16進数(Hexadesimal) 0x <hex digits) 0x6A

 

 

☆変数の種類
Type size 最小値 最大値
char 8bits -128 127
signed char 8bits -128 127
unsigned char 8bits 0 255
int 16bits -32,768 32767
unsigned int 16bits 0 65535
short 16bits -32,768 32767
unsigned short 16bits 0 65535
short long 24bits -8388608 8388607
unsigned short long 24bits 0 16777215
long 32bits -2147483648 2147483647
unsigned long 32bits 0 4292967295

float 32bits 2-126= 2 128*(2-2-15)=
1,17549435e-38 6,80564693e+38
double 32bits 2-126= 2 128*(2-2-15)=
1,17549435e-38 6,80564693e+38

 

 

☆端子の名前
VDD==+ VSS==- OSC1、2==セラロック

 

 

☆MCLR リセット回路
MCLRピンがLレベルになるとPICがリセットされます

5Vに対して、10KΩいれます

 

 

☆関係演算子「1」
イコール=「=」の右の数値を「=」の左に代入上書きする
port_data = PORTA; PORTAの内容をport_dataに書き写す
PORTB =port_data; port_dataの内容をPORTBに書き写す
C= A+B; の場合はAとBを足した結果をCに代入すること
イコールイコール== 等しいを表す場合はA==B
足す、加算 + 引く、減算 ー
かける、乗算 * 割る、除算 /
[矢印]
aはbより上 [a > b]
aはbより下 [a < b]
aはb以上 [a >= b]
aはb以下 [a <= b]
aとbが等しい [a == b]
aとbは等しくない [a != b]
インクリメント) [a ++] [a + 1]
デクリメント) [a --] [a - 1]
モジュロ算 a = b % c;
上の式の意味はbをcで割った余りaに代入するという意味

☆ビット演算子「2」
アンド aとbのビット毎のAND値を求める [a & b]
0 & 0 → 0
0 & 1 → 0
1 & 0 → 0
1 & 1 → 1
オア aとbのビット毎のOR値を求める [a | b]
0 | 0 → 0
0 | 1 → 1
1 | 0 → 1
1 | 1 → 1
エックスオーアール aとbのビット毎のXOR値を求める [a ^ b]
0 ^ 0 → 0
0 ^ 1 → 1
1 ^ 0 → 1
1 ^ 1 → 0
ノット NOT1の補数(ビット反転)aのビット毎の反転値を求める[ ~a ]
0→1
1→0
右シフト aのビット列をnビット右へシフト [a >> b]
ビット列を右に1シフト↘ 0b10101010>> 1
10101010←この値は破棄される
↘↘↘↘↘↘↘
空いたビットに0が入れられる →01010100
左シフト
aを左にbビットずらす(左シフト)[a << b]
ビット列を左に1シフト↘ 0b10101010<< 1
この値は破棄される  →10101010
↙↙↙↙↙↙↙
01010100←空いたビットに0が入れられる

☆関数の注意点
*オープンしたものはクローズする
*カウンタなどの場合は初期化リセットなどをする
*関数の後はセミコロン、

☆コンフィギュレーションビット fuses
18f2320例
クロック発振モード指定
LP 4MHz以下の外部発振振動子
XT 200MHz
HS 4MHZ以下の外部発振振動子
EC_IO 外部クロックRA6有効
EC 外部クロックCLKO
H4 PLL付XTモード
INTRC_IO 内部クロック RA6、RA7有効
INTRC 内部クロック CLKOとRA7有効
RC_IO 外付けRC発振 RA6有効
RC 外付けRC発振 CLKO有効

NOWDT ウォッチドッグタイマを使用しない
WDT ウォッチドッグタイマを使用する
WDT1~WDT32768 ウォッチドッグタイマのポストスケーラの選択1;1から1:32768まで
NOPUT パワーアップタイマを使用しない
PUT パワーアップタイマを使用する(通常)
MCLR有効化
NOMCLR MCLRを使わずRE3の入力ピンとする
MCLR MCLRを使用する

PROTECT コードをプロテクトする
NOPROTECT コードをプロテクトしない
NOBROWNOUT ブラウンアウトリセットをしない
BROWNOUT ブラウンアウトリセットをする
内部外部切り替え
NOIESO クロック内部外部切り替え無効
IESO クロック内部外部切り替え有効化
クロックモニタ
NOFCMEN クロック監視無効
FCMEN クロック監視を有効とする
低電圧プログラム
LVP 低電圧プログラムをする
NOLVP 低電圧プログラムをしない
バックグラウンド
デバッグ DEBUG ICDデバッグ機能を使う
NODEBUG ICDデバッグ機能を使わない
EEPROMデータの
プロテクト WRT EEPROMデータプロテクトをする
NOWRT EEPROMデータプロテクトをしない
BOR電圧選択
BORV20,BORV27 それぞれのBOR電圧を2.0V.2.7V.4.2V.4.5V
BORV42.BORV45 とする
CCP2の出力ピン指定
CCP2C1
CCP2B3
プログラムメモリの
書き込み保護 EBTR ブロック0書き込み保護有効
EBTRB ブロック1書き込み保護有効
NOEBTR ブロック0書き込み保護無効
NOEBTRB ブロック1書き込み保護無効
メモリプロテクト
CRB ブート部プロテクト有効
CRD データEEPROM部プロテクト有効
NOCRB ブート部プロテクト無効
NOCRD データEEPROM部プロテクト無効
書き込み保護
WRTB ブート部書き込み禁止
WRTC コンフィギュレーション部書き込み禁止
WRTD データEEPROM部書き込み禁止
NOWRTB ブート部書き込み許可
NOWRTC コンフィギュレーション部書き込み許可
NOWRTD データEEPROM部書き込み許可
PORTBアナログ入力
PBADEN PORTBのアナログ入力有効化
NOPBADEN PORTBのアナログ入力無効化
スタック異常のリセット
STVREN スタック異常でリセットする
NOSTVREN スタック異常でもリセットしない

Cプログラム

ホワイル while(継続条件){
while 処理


例えば「Aが3以下の間、Aに1を足していく」
という処理は下記のようになる
while(a<=3)
{
a =a+1;
}
aに最初1が入っていたらwhile文の中でaは右の 1回目 a = 2
ようになり、3回目の処理が終わるとwhile文は 2回目 a = 3
終了して、プログラムはその後の処理に移行 3回目 a = 4

ドゥ do{
do 処理;
}while(ループ条件);
do命令は処理を実行後while命令でループ条件を判定するのに対し
while命令はループ条件を判定した結果で処理を行いループするという違いが
あります

 

フォア for(初期化式;継続条件式;変化式){
for 処理

初期化式の値が変化式によって変化した後、
継続条件式が真ならば処理を繰り返し実行
継続条件式が偽ならば、処理は実行されない
使用例(10回繰り返す)
for(a=0 ; a<10 ; a++){
b=a*2;
}

 

 

イフ if(条件式){
if }
条件式が真ならば処理を実行
使用例
if(a<10){
b=a*2;
}

エルス if(条件式){
else 処理1
}else{
処理2
}
条件式が真なら処理1を実行、偽なら処理2を実行
使用例
if(a<10){
b=a*2;
}else{
b=a*4;
}

 

イフ、エルス
if,elseの組み合わせ
if文の中にさらにif文を入れて、細かく分岐させることが出来る、
その場合、左のコードを右のように省略して記述させることが出来る

if(条件式1){ if(条件式1){
処理1 処理1
}else{ 右 }else if(条件式2){
if(条件式2){ → 処理2
処理2 }else{
}else{ 処理3
処理3 }
}
}

 

スイッチ
switch switch(条件式){ 条件式の値が
判定式1なら処理1を実行
case判定式1: 判定式2なら処理2を実行
処理1 ↓
break; 判定式nなら処理nを実行
case判定式2: どれにも該当しなければ処理Xを実行
処理2
break; ☆使用例
switch(moji){
↓ case'A' :
a=10;
case判定式n: break;
処理n case'B' :
break; a=20;
default; break;
処理x default :
break; a=0 :
} break;
}
switch文の中でbreakを付け忘れると以下のようなことになるので注意
意図的な形で使うのであればよい

下記の状態PGと上との違いは処理’A’のあと処理’Bに移る形なる
上のPGは’A’処理後breakがあるので処理を完全に抜けるので、
そのまま下に移る
switch(moji){
case'A' :
a=10;
case'B' :
a=20:
default :
a=0;
}

 

 

プリスケーラ
bit2-0: PS2 : PS1 : PS0プリスケーラレート選択ビット
PS2  PS1  PS0  TMR0  WDT
0 0 0 1:2 1:1
0 0 1 1:4 1:2
0 1 0 1:8 1:4
0 1 1 1:16 1:8
1 0 0 1:32 1:16
1 0 1 1:64 1:32
1 1 0 1:128 1:64
1 1 1 1:256 1:128

コンテニュー continue命令は無条件にループ処理の最後に処理を移行させます
continue for(n=0;n<10;n++) break命令はループをの外側に脱出するのに対し、continue命令は
→{ ループの最後に飛びますから、条件が成立していれば更にループ
処理 を繰り返す処理を続行します
if(S=0)continue;  ↓
処理
.
.
↑}   ←
次の処理

ifの入れ子
下記、例1のPGはSWを押すと、消えるようなPGですが例1はそのまま下に流れて0110の後に1111が入って
しまうため、うまくいきません、そのため入れ子例2の形になります

例1 例2
while(1) while(1)
{ {
PORTB = 0b0110; if(RA0 == 0)
}else{ {
PORTB = 0b1111; PORTB = 0b0110;
} }else{
if(RA1 == 0) if(RA1 == 0)
{ {
PORTB = 0b1001; PORTB = 0b1001;
}else{ }else{
PORTB = 0b1111; PORTB = 0b1111;
}
}