ATMELのAT-Tiny26,ATmega,R8Cなどのワンチップマイコン,C言語,JAVAなどのプログラミング言語の入門のためのページです.サンプルプログラムを中心に紹介します.他にもLinixや数学ソフトなどの紹介も行います.

このブログを検索

あなたは 番目のお客様です.

2008年12月15日月曜日

マイコンによる電子オルゴール


ATtiny2313 を使った電子オルゴールのアセンブラプログラムです
ATtiny26やAT1200などでもマイナーな修正で動くはずです.
ATtiny2313の設定はInt. Osc. 8MHzで,
PB0とGNDに圧電サウンダを接続するとできあがりです.
曲はきよしこの夜
タイマ/カウンタ0を使い周期的にon,offすることにより音程を作ります
C(ド) DM(レ♭) D(ミ) DP(ミ♯) E(ファ)などのラベルは音程を与えます.
A5の値が音の長さ(音符)を決めます.
周波数はF=8M/64/2/(256-A2)となります
.DEF A1 =R16
.DEF A2 =R17
.DEF A3 =R18
.DEF A4 =R19
.DEF A5 =R20
.DEF CNT1 =R21
.DEF CNT2 =R22
.DEF CNT3 =R23
.INCLUDE "tn2313def.inc"
RJMP RESET ;各種リセット
RETI ;EXT_INT0 ;外部割り込み要求0
RETI ;EXT_INT1 ;外部割り込み要求1
RETI ;TIM1_CAPT ;タイマ/カウンタ1 捕獲(キャプチャ)発生
RETI ;TIM1_COMPA ;タイマ/カウンタ1 比較A一致
RETI ;TIM1_OVF ;タイマ/カウンタ1 オーバーフロー
RJMP T_INT ;タイマ/カウンタ0 オーバーフロー
RETI ;USART_RXC ;USART 受信完了
RETI ;USART_UDRE ;USART 送信バッファ空き
RETI ;USART_TX ;USART 送信完了
RETI ;ANA_COMP ;アナログ比較器出力遷移
RETI ;PCINT ;ピン変化割り込み要求
RETI ;TIM1_COMPB ;タイマ/カウンタ1 比較B一致
RETI ;TIM0_COMPA ;タイマ/カウンタ0 比較A一致
RETI ;TIM0_COMPB ;タイマ/カウンタ0 比較B一致
RETI ;USI_STRT ;USI 開始条件検出
RETI ;USI_OVF ;USI カウンタ オーバーフロー
RETI ;EE_RDY ;EEPROM操作可
RETI ;WDT_OVF ;ウォッチドッグ計時完了
;
RESET: LDI R16,LOW(RAMEND) ;RAM最終アドレス下位を取得
OUT SPL,R16 ;スタック ポインタ(下位)を初期化

SBI DDRB,0 ;PB0 Output
LDI A1,0B00000011 ;CLK/64
OUT TCCR0B,A1
LDI A1,0B00000010 ;T0 Ovf Int Enable
OUT TIMSK,A1
SEI ;All Int Enable

KIYOSHI:
;0<=A5<256
;300 -240
;150 -120
LDI A5,120
RCALL G
LDI A5,40
RCALL A
LDI A5,80
RCALL G
LDI A5,240
RCALL E
LDI A5,120
RCALL G
LDI A5,40
RCALL A
LDI A5,80
RCALL G
LDI A5,240
RCALL E
LDI A5,160
RCALL DD
LDI A5,80
RCALL DD
LDI A5,240
RCALL H
LDI A5,160
RCALL CC
LDI A5,80
RCALL CC
LDI A5,240
RCALL G
;2
LDI A5,160
RCALL A
LDI A5,80
RCALL A
LDI A5,120
RCALL CC
LDI A5,40
RCALL H
LDI A5,80
RCALL A
LDI A5,120
RCALL G
LDI A5,40
RCALL A
LDI A5,80
RCALL G
LDI A5,240
RCALL E
LDI A5,160
RCALL A
LDI A5,80
RCALL A
LDI A5,120
RCALL CC
LDI A5,40
RCALL H
LDI A5,80
RCALL A
LDI A5,120
RCALL G
LDI A5,40
RCALL A
LDI A5,80
RCALL G
LDI A5,240
RCALL E
;3
LDI A5,160
RCALL DD
LDI A5,80
RCALL DD
LDI A5,120
RCALL FF
LDI A5,40
RCALL DD
LDI A5,80
RCALL H
LDI A5,240
RCALL CC
LDI A5,160
RCALL EE
LDI A5,80
RCALL NOS
LDI A5,80
RCALL CC
RCALL G
RCALL E
LDI A5,120
RCALL G
LDI A5,40
RCALL F
LDI A5,80
RCALL D
LDI A5,240
RCALL C
LDI A5,160
RCALL C
LDI A5,80
RCALL NOS
RJMP KIYOSHI

T_INT: IN A3,SREG
SBRS A4,0
RJMP ON
OFF: CLR A4
CBI PORTB,0
RJMP P1
ON: SER A4
SBI PORTB,0
P1: OUT TCNT0,A2
OUT SREG,A3
RETI
;
NOS: CLI ;NO SOUND
RJMP L1
CM: LDI A2,129
OUT TCNT0,A2
RJMP L1
C: LDI A2,136
OUT TCNT0,A2
RJMP L1
DM: LDI A2,143 ;CP>>DM
OUT TCNT0,A2
RJMP L1
D: LDI A2,150
OUT TCNT0,A2
RJMP L1
DP: LDI A2,156
OUT TCNT0,A2
RJMP L1
E: LDI A2,161
OUT TCNT0,A2
RJMP L1
F: LDI A2,166
OUT TCNT0,A2
RJMP L1
FP: LDI A2,172
OUT TCNT0,A2
RJMP L1
G: LDI A2,176
OUT TCNT0,A2
RJMP L1
GP: LDI A2,181
OUT TCNT0,A2
RJMP L1
A: LDI A2,185
OUT TCNT0,A2
RJMP L1
AP: LDI A2,189
OUT TCNT0,A2
RJMP L1
H: LDI A2,193
OUT TCNT0,A2
RJMP L1
CC: LDI A2,196
OUT TCNT0,A2
RJMP L1
CCP: LDI A2,200
OUT TCNT0,A2
RJMP L1
DD: LDI A2,203
OUT TCNT0,A2
RJMP L1
DDP: LDI A2,206
OUT TCNT0,A2
RJMP L1
EE: LDI A2,209
OUT TCNT0,A2
RJMP L1
FF: LDI A2,211
OUT TCNT0,A2
RJMP L1
L1: MOV CNT1,A5
LOOP1: LDI CNT2,150
LOOP2: LDI CNT3,80 ;100*10/8
LOOP3: NOP
NOP
DEC CNT3
BRNE LOOP3
DEC CNT2
BRNE LOOP2
DEC CNT1
BRNE LOOP1
CLI
MOV CNT1,A5
L2: LDI CNT2,80 ;100*10/8
L3: NOP
NOP
DEC CNT2
BRNE L3
DEC CNT1
BRNE L2
SEI
RET

2008年11月27日木曜日

tiny2313でGPSデータを液晶に表示する


秋月のGPSレシーバモジュールキット(GPS-52)とマイコンATtiny2313,液晶SC1602BSを使い,
約2秒ごとに緯度経度を表示します.
GPS-52から出力されるシリアルデータの文字列の中から"36"の文字列を見つけ,
液晶の上段に緯度を,下段に経度を表示します.時々別な文字データが表示されます.
電源はATtiny2313とSC1602BSに単3×4を使い,GPS-52に3.3Vレギュレータ(TA48M033F)を
使います.またGPS-52でバックアップバッテリーは使わず,BATTピンもレギュレータの
3.3Vに接続しました.
Tiny2313の設定は,外部発振Ext Osc. 8MHz:Start-up time:6CK+64ms,分周なし.
GPS-52は,ビットレートが9600です.

Tiny2313とSC1602BSのピン接続は以下の通り.
PB0--RS
PB1
PB2--E
PB3
PB4--DB4
PB5--DB5
PB6--DB6
PB7--DB7
GND--R/W,Vo,Vss
5V--Vdd

Tiny2313とGPS-52のピン接続

RXD(PD0)--SDO
TXD(PD1)--RDO
GND--GND
3.3V--BATT,VCC
Tiny2313のシリアル入出力とGPS-52のシリアル入出力との間は,直接つなげてしまいました.

GPS-52は標準で測地系がTOKYOになってますが,googleマップなどでは測地系はWGS-84です.
測地系をWGS-84に切り替えるには,コマンドが必要です.
GPS-52は電源投入後、3秒間通信が禁止されるので注意すること.プログラムでは,約3秒
待ってから$PSRF106,21*0F<CR><LF>のコマンドを送っています.


以下Tiny2313のソースプログラム

/* gps2313.c 
Tiny2313,GPS-52,液晶SC1602BSを接続し約2秒ごとに緯度経度を表示する
注意:GPS-52は電源投入後、3秒間通信が禁止される。GPSとマイコンが同時オンの場合,
測地系がWGS-84に変更できない
約3秒待ってから$PSRF106,21*0F<CR><LF>

Tiny2313:外部発振Ext Osc. 8MHz:Start-up time:6CK+64ms
分周なし
ビットレート:9600,U2X=0
Tiny2313--SC1602BSのピン接続
PB0--RS
PB1
PB2--E
PB3
PB4--DB4
PB5--DB5
PB6--DB6
PB7--DB7
GND--R/W,Vo,Vss
5V--Vdd

Tiny2313--GPS-52のピン接続

RXD(PD0)--SDO
TXD(PD1)--RDO
GND--GND
3.3V--BATT,VCC
Tiny2313の電源は単3×4,GPS-52の電源はレギュレータ3.3V(TA48M033F)を使う
*/

#include <avr/io.h>
#include <avr/interrupt.h>
uint16_t a;//aは16ビット整数でマイクロ秒単位のカウントを行うa<65535

ISR(TIMER0_OVF_vect){ //タイマカウンタ0のオーバーフローで呼ばれる
a+=256;
}
//マイクロ秒単位の待ち
void delay(uint16_t t){
TCNT0=0;
a=0;
while(a+TCNT0<t){}
}
void en(void){
PORTB|= 1<<2;
delay(20);
PORTB&=~(1<<2);
delay(20);
}

//SC1602BSへのコマンド
void cmnd(char i){
PORTB=i&(0B11110000);
en();
PORTB=(i<<4)&(0B11110000); //i<<4は4ビット左シフト &はビット間のAND
en();
delay(40);
}

//データの書き込み
void dat(char i){
PORTB=i&(0B11110000);
PORTB|= 1<<0; //RS
en();
PORTB=(i<<4)&(0B11110000);
PORTB|= 1<<0; //RS
en();
delay(40);
}

void Utx(uint8_t data) //マイコンがデータを送信する
{
while ( !(UCSRA & (1<<UDRE)) );
UDR = data;
}

uint8_t Urx(void) //マイコンがデータを受信する
{
while ( !(UCSRA & (1<<RXC)) );
return UDR;
}


int main( void )
{
uint8_t dat1,i,c1,c2;
uint16_t baud;
baud=8000000/16/9600-1;//ボーレートの計算
UBRRH = (uint8_t)(baud>>8);//ボーレート上位
UBRRL = (uint8_t)baud; //ボーレート下位
UCSRC = (1<<USBS)|(3<<UCSZ0);
UCSRB = (1<<RXEN)|(1<<TXEN);//送受信可

DDRB = 0xFF; //PB all output
TCCR0B=0B00000010; //CK/8
TIMSK|= 1<<TOIE0; //T0 Ovf Int Enable
sei(); //All Int Enable
delay(20000); //Wait 20ms
PORTB=0B00110000;
en();
delay(5000); //Wait 5ms
en();
delay(100); //Wait 100Us
en();
delay(50); //Wait 50Us
PORTB=0B00100000; //Function set
en();
delay(50); //Wait 50Us
cmnd(0B00101100); //N=1:2lines F=1:10dots
cmnd(0B00001100); //Display On Off Control
cmnd(0B00000110); //Entry Mode Increment

//測地系をWGS-84に変更 $PSRF106,21*0F<CR><LF>
for(i=0;i<60;i++)delay(50000); //Wait 3s
Utx('$');
Utx('P');
Utx('S');
Utx('R');
Utx('F');
Utx('1');
Utx('0');
Utx('6');
Utx(',');
Utx('2');
Utx('1');
Utx('*');
Utx('0');
Utx('F');
Utx(0X0D);//CR
Utx(0X0A);//LF

while(1){
for(i=0;i<40;i++)delay(50000); //Wait 2s
cmnd(0B00000001); //Clear LCD
while(1){ //"36"の文字列を見つける
c2=Urx();
if(c2=='6' && c1=='3')break;
c1=c2;
}//while()
dat(c1);
dat(c2);
for(i=0;i<10;i++){dat1 = Urx();dat(dat1);}
cmnd(0XC0); //Lower Line
for(i=0;i<12;i++){dat1 = Urx();dat(dat1);}
}//while(1)
}

2008年11月21日金曜日

Tiny2313でカウンタと分周

Tiny2313のint0の割り込みを使いカウントを行い結果をPORTBに表示します
Tiny2313のヒューズの設定は,内蔵RC8MHz,8分周なしとしました.
#include <avr/io.h>
#include <avr/interrupt.h>

uint8_t a;  //カウンタ値を記録
ISR(INT0_vect){ //INT0で呼ばれる割り込み
a++;
PORTB=a;
}


int main( void )
{
DDRB = 0xFF;  //PB all output
DDRD = 0x00;  //PD all input
MCUCR|= 1<<ISC00;
MCUCR|= 1<<ISC01; //Int0 の立ち上がりで割り込み

a=0x00;

GIMSK|= 1<<INT0; //Interrupt 0 enable
sei();   //All Int Enable

while(1){}
}

2008年9月16日火曜日

Tiny2313を使ってシリアル通信とEEP-ROMリーダ









Tiny2313を使ってシリアル通信でPCと接続するサンプルプログラムです.
PC側からaを入力すればPB0に接続したLEDが点灯し,
bを入力すればLEDが消灯し,
c-zを入力すればPC側に大文字を返します.
Tiny2313は8MHzの外部発振で,分周なしの設定にしておきます.
PC側の設定は,ボーレート19200,データ8ビット,
パリティnone,ストップ1bitフロー制御noneにしておきます.
/*シリアル通信でPCとTiny2313を接続する
comtn2313b.c(通信の初期設定をmainに入れた)
Ext8MHz,分周なし,ビットレート:19200,U2X=0
a:LED on b:LED off c-z:大文字を返す
*/

#include <avr/io.h>

void Utx(unsigned char data) //マイコンからデータを送信する
{
while ( !(UCSRA & (1<<UDRE)) );
UDR = data;
}

unsigned char Urx(void) //マイコンがデータを受信する
{
while ( !(UCSRA & (1<<RXC)) );
return UDR;
}

int main(void){
unsigned char dat;
unsigned int baud;
baud=8000000/16/19200-1;//ボーレートの計算
UBRRH = (unsigned char)(baud>>8);//ボーレート上位
UBRRL = (unsigned char)baud; //ボーレート下位
UCSRC = (1<<USBS)|(3<<UCSZ0);
UCSRB = (1<<RXEN)|(1<<TXEN);//送受信可

DDRB=0B00000001; //PD0(Output)
while(1){
dat = Urx();
if(dat=='a')PORTB=0B00000001; //PD0 H(LED:on)
if(dat=='b')PORTB=0B00000000; //PD0 L(LED:off)
if(dat>'b'){ //c-zは大文字を返す
dat=dat-'a'+'A';
Utx(dat);
Utx(0x0a); //LF
Utx(0x0d); //CR
}
}//while(1)
}


続いて以下は,Tiny2313とEEP-ROM(ATMEL24C256)をつないだROMリーダです.
パソコンで数値,sと入力するとEEP-ROMの先頭からn個のデータを
RS-232Cを通してPCに出力します.
/*
EEP-ROM リーダーTiny2313(ExtOsc8MHz,クロックを8分周しない)
ATMEL24C256の場合8ビット×32K,32K=2^15=32768
次のように最初のアドレス:データを続けて表示する
0000:000102030405060708090A0B0C0D0E0F
0010:101112131415161718191AFFFFFFFFFF
0020:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
ボーレートは19200
マイコンとADM3202,24C256の接続は以下の通り
(Tiny2313)RXD--(ADM3202)R1OUT
(Tiny2313)TXD--(ADM3202)R1IN
(Tiny2313)PB0--(24C256)SCL(1kΩでプルアップ)
(Tiny2313)PB1--(24C256)SDA(1kΩでプルアップ)
*/

#include <avr/io.h>

//マイクロ秒単位の待ち(t<256)
void delay(int8_t t){
TCCR0B=0B00000010; //タイマカウンタ0の分周をセットCK/8
TCNT0=0;
while(TCNT0<t){}
}

void start(void){
PORTB|= 1<<1;// set PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<1); // clear PB1
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void stop(void){
PORTB&=~(1<<1); // clear PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB|= 1<<1;// set PB1
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void Hclk(void){//SDAがhighで1クロック
PORTB|= 1<<1;// set PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void Lclk(void){//SDAがlowで1クロック
PORTB&=~(1<<1); // clear PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<0); // clear CLK
delay(5);
}

//データ読み込み
uint8_t read(uint16_t add){
uint8_t i;
uint16_t k;
start();
for(k=0B100000000;k>0;k=k>>1)if(k & 0B101000000)Hclk();
else Lclk();
i=add/256; //上位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK

i=add%256; //下位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK
start();
for(k=0B100000000;k>0;k=k>>1)if(k & 0B101000010)Hclk();
else Lclk();
DDRB&=~(1<<1); //PB1を入力に変更
i=0;
for(k=0B10000000;k>0;k=k>>1){
PORTB|= 1<<0; // set CLK
delay(5);
if(PINB & (1<<1))i+=k;
PORTB&=~(1<<0); // clear CLK
delay(5);
}
DDRB|= 1<<1; //PB1を出力に変更
Hclk();//NO ACK
stop();//Stop
return i;
}

void Utx(unsigned char data) //マイコンからデータを送信する
{
while ( !(UCSRA & (1<<UDRE)) );
UDR = data;
}
unsigned char Urx(void) //マイコンがデータを受信する
{
while ( !(UCSRA & (1<<RXC)) );
return UDR;
}

int main(void)
{
uint8_t x,y,dat;
uint16_t i,j,k=0,baud=25; //ボーレートの計算=8000000/16/19200-1
UBRRH = (unsigned char)(baud>>8);//ボーレート上位
UBRRL = (unsigned char)baud; //ボーレート下位
UCSRC = (1<<USBS)|(3<<UCSZ0);
UCSRB = (1<<RXEN)|(1<<TXEN);//送受信可

DDRB=0B00000011; //PB0,PB1:OUT
TIMSK|= 1<<TOIE0; //タイマカウンタ0のオーバーフロー割り込みを許可する
//sei(); //All Int Enable 割り込みを可能にする

while((dat = Urx())!='s')k=k*10+dat-'0';//'s'が入力されるまで待つ

for(i=0;i<k/16;i++){
//最初のアドレス(i*16)を表示
//16進のiと0を表示するのはi*16を表示するのと同じである
y=i/256;
if(y>9)Utx('A'+y-10);
else Utx('0'+y);
y=(i%256)/16;
if(y>9)Utx('A'+y-10);
else Utx('0'+y);
y=i%16;
if(y>9)Utx('A'+y-10);
else Utx('0'+y);
Utx('0');
Utx(':');
for(j=0;j<16;j++){

x=read(i*16+j); //Read EEP
y=x/16;
if(y>9)Utx('A'+y-10);
else Utx('0'+y);
y=x%16;
if(y>9)Utx('A'+y-10);
else Utx('0'+y);
}//for i
Utx(0x0a); //LF
Utx(0x0d); //CR
}//for i
while(1){}
}

2008年9月15日月曜日

AVR入門

AVRDRAGONを使う
AVRマイコンの書き込みは,AVRISPmkIIが入門用として使いやすいです.
AVRDRAGONを使ってみました.AVRDRAGONはボードのみです.AVRISPmkIIのようにUSBケーブルや8ピンケーブル,ソフトのCDが付いていません.ただAVRISPmkIIを使ったことがある人ならば,特に問題なく使えると思います.
AVRstudioをインストールしていれば,新しくインストールするものはありません.
AVRDRAGONを接続すれば,ドライバのインストールが始まります.
mkIIと同様に6ピンのISPとターゲットのマイコンを接続すれば,ISPプログラミングが可能です.
AVRDRAGONはUSB電源(5V)のピンを持っています.このVCCをマイコンに供給すれば,マイコン側で電源は不要です.
一方で過去に作ったマイコンのプログラマターゲットを使いたい場合,両者の電源がバッティングします.私は3V電源のマイコンのプログラマターゲットを使っていました.
そこでAVRDRAGONのVCCとISP6ピンのVTGを100Ωの抵抗で結びました.こうすれば,電源無しのターゲットでも3V電源のターゲットでも同じように使えます.
写真の左が,AVRDRAGONの裏側,右上がtiny2313のターゲット,右下がmega88のターゲットです.










AVRのアセンブラの比較
よく使うAT90S1200,AT90S2313,ATTINY2313,ATTINY26で3個のLEDを点滅させるアセンブラプログラムの比較を行いました.
定義ファイルはそれぞれ,"1200def.inc","2313def.inc","tn2313def.inc","tn26def.inc"です.それぞれ割り込みベクタの数が違うのでRETIの数が異なります.
AT90S1200にはスタックポインタの記述がありません.AT90S2313とATTINY2313のスタックポインタ名はSPLで,ATTINY26のスタックポインタ名はSPです.
またATTINY2313にはクロックの分周機能が着いていて,これがセットさたままだと速度が8分の1になるので気をつけましょう.
以下AT90S1200,AT90S2313,ATTINY2313,ATTINY26のソースプログラムです.

;90S1200
.include "1200def.inc"
.def CNT1 =R17
.def CNT2 =R18
.def CNT3 =R19
RJMP RESET
RETI
RETI
RETI
RESET: LDI R16,0B11111111
OUT DDRD,R16
MAIN:
LDI R16,0B00001000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00010000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00100000
OUT PORTD,R16
RCALL TIME
RJMP MAIN
TIME:
LDI CNT1,10
LOOP1:
LDI CNT2,255
LOOP2:
LDI CNT3,255
LOOP3:
NOP
NOP
DEC CNT3
BRNE LOOP3
DEC CNT2
BRNE LOOP2
DEC CNT1
BRNE LOOP1
RET


;90S2313 led2313.asm
.include "2313def.inc"
.def CNT1 =R17
.def CNT2 =R18
.def CNT3 =R19
RJMP RESET
RETI ;1
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI ;$9
RETI ;$A
RESET: LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R16,0B11111111
OUT DDRD,R16
MAIN:
LDI R16,0B00001000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00010000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00100000
OUT PORTD,R16
RCALL TIME
RJMP MAIN
TIME:
LDI CNT1,10
LOOP1:
LDI CNT2,255
LOOP2:
LDI CNT3,255
LOOP3:
NOP
NOP
DEC CNT3
BRNE LOOP3
DEC CNT2
BRNE LOOP2
DEC CNT1
BRNE LOOP1
RET


;Tiny2313 ledtn2313.asm
;システム クロック前置分周器に注意
.include "tn2313def.inc"
.def CNT1 =R17
.def CNT2 =R18
.def CNT3 =R19
RJMP RESET
RETI ;1
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI ;$9
RETI ;$A
RETI
RETI
RETI
RETI
RETI
RETI ;$10
RETI ;$11
RETI ;$12
RESET: LDI R16,LOW(RAMEND)
OUT SPL,R16
LDI R16,0B11111111
OUT DDRD,R16
MAIN:
LDI R16,0B00001000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00010000
OUT PORTD,R16
RCALL TIME
LDI R16,0B00100000
OUT PORTD,R16
RCALL TIME
RJMP MAIN
TIME:
LDI CNT1,10
LOOP1:
LDI CNT2,255
LOOP2:
LDI CNT3,255
LOOP3:
NOP
NOP
DEC CNT3
BRNE LOOP3
DEC CNT2
BRNE LOOP2
DEC CNT1
BRNE LOOP1
RET


;Tiny26 ledtn26.asm
;スタックポインタ名はSPLではなくSP
;PB0,1,2にLEDを接続した
;PIN7(RESET)PIN15(AVCC)PIN16(GND)を開放しても動く
.include "tn26def.inc"
.def CNT1 =R17
.def CNT2 =R18
.def CNT3 =R19
RJMP RESET
RETI ;1
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI ;$9
RETI ;$A
RETI ;$B
RESET: LDI R16,LOW(RAMEND)
OUT SP,R16
LDI R16,0B00000111
OUT DDRB,R16
MAIN:
LDI R16,0B00000001
OUT PORTB,R16
RCALL TIME
LDI R16,0B00000010
OUT PORTB,R16
RCALL TIME
LDI R16,0B00000100
OUT PORTB,R16
RCALL TIME
RJMP MAIN
TIME:
LDI CNT1,10
LOOP1:
LDI CNT2,255
LOOP2:
LDI CNT3,255
LOOP3:
NOP
NOP
DEC CNT3
BRNE LOOP3
DEC CNT2
BRNE LOOP2
DEC CNT1
BRNE LOOP1
RET

2008年9月12日金曜日

マイコンと2進数

マイコンの中での数の扱い(2進数)に関するメモです
コンピュータ内部では数値はすべて2進数が基本となっている.電流が流れる状態を1(ON),電流が流れない状態を0(OFF)としている.1つの電流の状態を1ビット(byte)と呼ぶ.8ビットを1つのまとまりとして1バイト(byte)とする.

2進数を4桁ずつまとめると16進数になる.2進数をそのまま書くと長くなるので通常は16進数で表すことが多い.
0B0000=0X0
0B0001=0X1
0B0010=0X2
0B0011=0X3
.
.
0B1110=0XE
0B1111=0XF
先頭に0Bを付ける数は,2進数を意味する.16進数の場合,先頭に0Xや0$を付ける.

例題
次の2進数を16進数に変換してみよう.
0B01001111(16進数 ) 0B11001001(16進数 )

次の16進数の計算をやってみよう.10進数にも変換してみよう.
0X1E
+0XAA
-------


符号なしと符号ありの2進数
2進数には符号なしと符号ありの2種類の表記方法がある.自分が扱っている数が,符号なしなのか符号ありなのかを注意する必要がある.

符号なし2進数
符号なし2進数では,8ビットで0から255まで(256通り)の数を表現できる.

2進数 00000000(10進数で0)
00000001(1)
00000010 (2)
00000011(3)
...途中略...
11111111( 255)

次の2進数の計算をやってみよう.
01001011
+10011001
---------



10011001
-01101011
---------


符号あり2進数
符号あり2進数では,8ビットで-128から127まで(256通り)の数を表現できる.

2進数 10000000(10進数で-128)
10000001 (-127)
10000010(-126)
10000011 (-125)
...途中略...
11111111(-1)
00000000(0)
00000001(1)
00000010(2)
00000011(3)
...途中略...
01111111(127)

次の符号あり2進数の計算をやってみよう.
01001011
+10011001
---------


C言語では,文字型変数charは1バイトの符号あり2進数である.従って次の処理で変数はマイナスの値(-128)となる.
char i=127;
i++;

2008年8月8日金曜日

R8C/Tiny29(サンハヤトのMB-8C29)の評価

サンハヤトのMB-8C29を使ってみました.基本的にはSR8C15CPと同様に使うことができました.MB-8C29は,20MHz発振子が内蔵されています.リセット回路(RとC)は付いていません.せっかくだからリセット端子にRとCを付けてくれれば良いのにと思います.またLEDがP1_1ピンに付いています.R8C29ではピンの駆動電流を設定するdrrがなくなっています.

実行手順のメモ(C言語によるプログラミングの場合)

1.実行プログラムの作成
HEWをインストールする.
HEWをインストールしたフォルダの下にあるinc30というフォルダに,sfr_r829.h(Tiny15の定義ファイル)を入れておく.
HEWを起動する.
新規プロジェクトワークスペースを作成する.
プロジェクトの種類:Application
CPUSeries:R8C/Tiny

CPUGroup:29
Releaseコンフィグレーションを選択する(HEWのウィンドウの上部ボックスでReleaseかDebugを選択できるようになっている)
Cのソースファイルを編集する

サンプルプログラムの例
//test.c for MB-R8C29
//

#include "sfr_r829.h" // R8C29 SFR定義ファイル


void main(void) // メイン関数
{
int i,j,k;

// drr1 = 1;入れるとコンパイルできない

pd1_1 = 1; //ポートP1_1 出力
p1_1 = 1; //ポートP1_1 H

//メインクロック切替
prc0 = 1;
cm13 = 1;
cm05 = 0;
cm06 = 0;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
ocd2 = 0;
prc0 = 0;
//
while(1){
for(i=0;i<10;i++)for(j=0;j<25
0;j++)for(k=0;k<250;k++){}
p1_1 = 0;
for(i=0;i<10;i++)for(j=0;j<250;j++)for(k=0;k<250;k++){}
p1_1 = 1;
}//while(1)
}

2.実行プログラムの書き込み
(R8C15から変更はありません)
M16C Flash Starterをダウンロードし適当な場所に置いておく.
書き込み回路とcomポートを接続し,回路の電源を入れる.このとき書き込み回路は,ブートモードにする.(モードセレクタPIN8をGND側に接続しておく.)

M16C Flash Starter(FlashSta.exe)を
実行し,
SelectProgram:InternalFlashMemory,
COMポートを選択する.
Referをクリックしfile pathにはプロジェクトのreleaseフォルダ内の○.motを選ぶ.
IDの空欄にすべてffを入れる.MCU TypeはR8Cを選択する.
E.P.Rをクリックすると消去,書き込みが自動的に行われる.









2008年8月3日日曜日

R8C/Tinyを使ったRS-232通信テストプログラム

R8C/Tinyを使ったRS-232(comポート)の通信テストプログラムです.プログラムの書き込みと通信に別々のCOM端子を使うなら,トラ技2005年4月号付録のR8C/15搭載マイコンボード(MB-R8CS:サンハヤト)が便利です.
テストプログラムでは,aを入力するとLED on,bを入力するとLED offします.
c-zを入力すると大文字を返します.
送信の関数名をtx()とするとコンパイルエラーが生じます.
通信ソフトにはハイパーターミナルかteratermを使います.

#include "sfr_r815.h"
void Utx(char data); // 1文字送信
char Urx(void); // 1文字受信

void main(void) // メイン関数
{
char dat;
// I/Oポートの初期設定
drr1= 1;//P1_1 駆動能力High設定
pd1_1= 1;//P1_1 出力モード設定
p1_1= 1;//P1_1 Hレベル設定(LED:消灯)

//メインクロックの切替
prc0 = 1;
cm13 = 1;
cm05 = 0;
cm06 = 0;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
ocd2 = 0;
prc0 = 0;

//UART0初期化
smd0_u0mr= 1; // シリアルI/Oモード選択ビット
smd2_u0mr= 1; //8ビットモード
te_u0c1= 1; //送信許可
re_u0c1= 1; //受信許可
u0brg= 0x40; //ビットレート(19200bps)
Utx('T');
Utx('E');
Utx('S');
Utx('T');
Utx(0x0a); //LF
Utx(0x0d); //CR

while(1){
dat = Urx();
if(dat=='a')p1_1=0; //P1_1 Lレベル(LED:on)
if(dat=='b')p1_1=1; //P1_1 Hレベル(LED:off)
if(dat>'b'){ //c以降は大文字を返す
dat=dat-'a'+'A';
Utx(dat);
}
}//while
}//main

void Utx(char data)
{
while(ti_u0c1 != 1); // データ転送待ち
u0tb = data; // 送信バッファにデータセット
}

char Urx(void)
{
char data; // 受信データ格納変数
char err; // エラーデータ格納変数
while (ri_u0c1 != 1) ; // 受信待ち
data = u0rbl; // 受信データの取り出し
err = u0rbh & 0xf0; // エラーデータの取り出し
return data;
}

2008年7月30日水曜日

R8C/TinyシリーズのリセットとIDコードチェック機能

R8C/Tinyシリーズのアセンブリ言語のサンプルプログラムを見るとリセットベクタのところに
.LWORD Start | 0FF000000h
という記述がある.リセットベクタにStartのアドレスを書いておけば,電源投入時(RESET)に,Start:の場所からプログラムが動き出す.だったら
.LWORD Start
だけでも動くはずだが,動かない.何故Startのアドレスと0FF000000hの論理和(|)なのか?ずっと疑問に思っていた.
R8Cのハードウェアマニュアルを読むと,「割り込み」の章で割り込みベクタの記述がある.各割り込みに対応した4バイトからなる割り込みベクタがある.割り込みが生じると,この割り込みベクタに書かれているアドレスに分岐する.
さらにこんな記述がある.「固定ベクタのベクタ番地(H)はIDコードチェック機能で使用します。詳細はフラッシュメモリ書き換え禁止機能を参照してください。」
ということでマニュアルの「フラッシュメモリ書き換え禁止機能」のところを読む.
すると7つの指定された固定ベクタの上位番地が,「ライタから送られてくるID コードとフラッシュメモリに書かれている7 バイトのID コードが一致するか判定する」ために使われているとのこと.Flash Starterで入力していたFFと,これらの値が一致しないとライタから送られてくるコマンドは受け付けないとのころ.なるほど.
そしてIDコードの格納番地の図にリセットベクタも書かれていて(注)となっている.
「(注)00FFFFh番地にはOFSレジスタが配置されています。OFSレジスタの詳細は「 OFSレジスタ」 を参照してください。」とのこと.
そして今度はOFSレジスタの項を読む.
OFS(オプション機能選択)レジスタとは,ROMコードプロテクトやウォッチドッグタイマなどの設定を行っている.そのアドレスが,0FFFFh番地つまりリセットベクタの上位番地だったのである.ややこしい共有はするな!と思いつつ納得.OFSレジスタは標準では全て1つまりFFの設定になっているので,OFSレジスタを変えずにリセットベクタの下位番地にStartのアドレスを書き込むためには,Startのアドレスと0FF000000hの論理和が必要だったわけである.ちなみに0FFFFh番地はアドレス空間の一番最後(おおとり)である.Flash StarterでFFを7回入力していた理由と,リセットベクタの0FF000000hの謎がやっととけた.

2008年7月29日火曜日

R8C/Tinyマイコン初めの一歩

R8C/Tinyシリーズは,実行プログラムの書き込み回路が簡単にできる点が良いと思います.ここではR8C/Tiny15(サンハヤトのSR8C15CP)を例に,書き込み回路を準備して実行するまでの手順を説明します.

1.準備
まずはマイコン(サンハヤトのSR8C15CP)を入手し,実行プログラムの書き込み回路とマイコンを動かすための回路を準備する.
必要なソフトをダウンロードする.(HEW+定義ファイル+Flash Starter)

実行プログラムの作成には,HEWが必要である.最新バージョンのHEW(V4.04)はTiny15にもTiny29にも対応している.またマイコン毎に異なる定義ファイルが必要である.(C言語によるプログラミングの場合xx.h)(アセンブラによるプログラミングの場合xx.inc) またCOMポート(RS-232C)から簡単な回路を使って実行プログラムの書き込みを行う場合,フラッシュプログラマ( M16C Flash Starter M3A-0806)が必要である.

2.実行プログラムの作成(C言語によるプログラミングの場合)
HEWをインストールしたフォルダの下にあるinc30というフォルダを見つけ,sfr_r815.h(Tiny15の定義ファイル)を入れておく.
HEWを起動する.
新規プロジェクトワークスペースを作成する.
プロジェクトの種類:Application
CPUSeries:R8C/Tiny
CPUGroup:15

Releaseコンフィグレーションを選択する(HEWのウィンドウの上部ボックスでReleaseかDebugを選択できるようになっている)
Cのソースファイルを編集する
またsect30.incのファイルで下記のようにROM開始位置を0c000Hに修正する

;---------------------------------------------------------------
; Near ROM data area
;---------------------------------------------------------------
.section rom_NE,ROMDATA
.org 0c000H
rom_NE_top:


全てをビルドすると,作成したプロジェクトのReleaseフォルダの中に実行プログラムXX.motができる.

3.プログラムの書き込み
M16C Flash Starterをダウンロードし適当な場所に置いておく.
書き込み回路とcomポートを接続し,回路の電源を入れる.このとき書き込み回路は,ブートモードにする.(モードセレクタPIN8をGND側に接続しておく.)

M16C Flash Starter(FlashSta.exe)を実行し,

SelectProgram:InternalFlashMemory,

COMポートを選択する. Referをクリックしfile pathにはプロジェクトのreleaseフォルダ内のXX.motを選ぶ. IDの空欄にすべてffを入れる.MCU TypeはR8Cを選択する. E.P.Rをクリックすると消去,書き込みが自動的に行われる.
書き込み回路











4.マイコンの実行
回路にマイコンをつけて電源を入れれば,動くはず.実行回路は,RUNモードにする.(モードセレクタPIN8をVCC側に接続しておく.)
LEDを使った回路の例

2008年7月19日土曜日

加速度センサを使う


加速度センサKXM52(秋月版)のX,Y2軸の加速度を
Tiny26で計測し液晶SC1602BSで表示します.
Tiny26は内部発振8MHzに設定します.
Int.RC Osc. 8MHz:Start-up time:6CK+64ms
注意:KXM52-1052の感度は,5Gに対してVCCの電圧変化とります.
下記プログラムでは,
4Gに対してVCCの電圧変化するような
プログラムになっていますので修正が必要です.

Tiny26とSC1602BSのピン接続は以下のとおりです.
PA0--RS
PA1
PA2--E
PA
3
GND
AVCC
PA4--DB4
PA5--DB5
PA6--DB6
PA7--DB7
GND--R/W,Vo,Vss
5V--Vdd
PB6(ADC9):OutX
PB5(ADC8):OutY

回路











ソースプログラム

#include <avr/io.h>
#include <avr/interrupt.h>
uint16_t a;//aは16ビット整数でマイクロ秒単位のカウントを行うa<65535

ISR (TIMER0_OVF0_vect){ //タイマカウンタ0のオーバーフローで呼ばれる
a+=256;
}
//マイクロ秒単位の待ち
void delay(uint16_t t){
TCNT0=0;
a=0;
while(a+TCNT0<t){}
}
void en(void){
PORTA|= 1<<2;
delay(20);
PORTA&=~(1<<2);
delay(20);
}

void cmnd(char i){
PORTA=i&(0B11110000);
en();
PORTA=(i<<4)&(0B11110000); //i<<4は4ビット左シフト &はビット間のAND
en();
delay(40);
}

//データの書き込み
void dat(char i){
PORTA=i&(0B11110000);
PORTA|= 1<<0; //RS:High
en();
PORTA=(i<<4)&(0B11110000);
PORTA|= 1<<0; //RS:High
en();
delay(40);
}

void disp(uint16_t ad){
uint16_t x;
if(ad<512){dat(0X2D);//-
ad=512-ad;}
else{dat(0X20);//SPACE
ad=ad-512;}
x=ad;
//-512 to 511 : -2 to +2 G
//x=(x*2000)/512; //bin to dec
x=(x*125)/32; //bin to dec
ad=x;
dat('0'+ad/1000);
ad%=1000;
dat(0X2E); //dot
dat('0'+ad/100);
ad%=100;
dat('0'+ad/10);
ad%=10;
dat('0'+ad);
dat(' '); //SPACE
dat('G'); //G
}

int main( void )
{
uint16_t i,ad;
DDRA = 0xFF;
DDRB=0X00; //全て入力
ADMUX=0B00101001; //AVCC LEFT ADJUST, ADC9
ADCSR=0B10000110; //SET ADCSR CK/64
TCCR0=0B00000010; // CK/8
TIMSK|= 1<<TOIE0; //T0 Ovf Int Enable
sei(); //All Int Enable

for(i=0;i<200;i++)delay(1000); //Wait 200ms
PORTA =0B00110000;
en();
delay(5000); //Wait 5ms
en();
delay(100); //Wait 100Us
en();
delay(50); //Wait 50Us
PORTA=0B00100000; //Function set
en();
delay(50); //Wait 50Us
cmnd(0B00101100); //N=1:2lines F=1:10dots
cmnd(0B00001100); //Display On Off Control
cmnd(0B00000110); //Entry Mode Increment

while(1){
cmnd(0B00000001); //Clear LCD
delay(2000); //Wait 2ms
ADMUX=0B00101001; //AVCC LEFT ADJUST, ADC9
ADCSR|= 1<<ADSC; //AD start
while(!(ADCSR & (1<<ADIF))){} //ADCSRのビット4(ADIF)が1になるまで待つ
ad=ADCL/64;
ad+=ADCH*4;
dat('X'); //X
dat(':'); //:
disp(ad);

cmnd(0XC0); //DDRAM=40 Lower Line
ADMUX=0B00101000; //AVCC LEFT ADJUST, ADC8(PB5)
ADCSR|= 1<<ADSC; //AD start
while(!(ADCSR & (1<<ADIF))){} //ADCSRのビット4(ADIF)が1になるまで待つ
ad=ADCL/64;
ad+=ADCH*4;
dat('Y'); //Y
dat(':'); //:
disp(ad);

for(i=0;i<500;i++)delay(1000); //Wait 500ms
}//while(1)
}


2008年7月18日金曜日

MATLABとOctaveを使う

MATLAB,Octave,scilabに関するメモ(未完成)
MATLABは数学処理ソフトである.MathematicaやMaximaが数式を変換したり,方程式の解を求めるのに対して,MATLABやOctaveはデータを処理するのに適している.MATLABは市販ソフトであるが,Octaveやscilabは(ほぼ)MATLAB互換のフリーソフトである.
非対角行列の逆行列を求める(最小二乗問題を解く)ときに便利である.
(以下Octaveの使用方法)
まずはダウンロードしてOctaveのアイコンをダブルクリックする.

作業領域に移動する.

MATLABでは一連の処理を○.mで保存しておけば,自動的に実行してくれます.プログラミング感覚で処理ができ,便利です.これをmファイルといいます.
Octave(GNU Octave)はMATLAB互換のフリーソフトです.MATLAB同様mファイルの実行が可能です.windows上で実行できるOctaveはOctave-Forgeというソフトです.

mファイルでは%以下はコメントです
既知の行列A,未知のベクトルX,既知のベクトルYの間にY=AXの関係があるときに次の処理でXを求めることができます
X=A\Y

2008年7月15日火曜日

マイコンでI2C-EEPROMの読み書きを行う


I2Cはプルアップされた2本の信号線による双方向通信方式です.
24CXXタイプのEEPROMは,SDAとSCLの2ピンでデータの読み書きができます.
ATMEL24C256は2.7-5.5Vの電圧で動き,8ビット×32768のデータが記録できます.
ATMEL24C256は書き込みに10-20msの時間が必要でこの間は,信号を受け付けません.

SDAピンがデータの書き込みと読み込みを兼ねます(2-wire Serial)
SDAピンはプルアップ(1kΩでVCCにつなげる)してPB2と接続します
SCLピンはプルアップ(1kΩでVCCにつなげる)してPB1と接続します
EEPROMを1個だけ使うのであればA0=0,A1=0としてGNDにつなげておくのが望ましいが,
無接続でも動きます.
WPピンはhighにすると書き込み不可です.GNDにつなげておくか,無接続でも動きます
SCLの立ち上がりでデータ書き込み,SCLの立ち下がりでデータ読み込みが行われます.

ATMEL24C256のピンの説明
VCC:電源のプラス
A0:Address Inputs(GNDまたは無接続)
A1:Address Inputs(GNDまたは無接続)
SDA:Serial Data:PB1
SCL:Serial Clock Input:PB0
WP:Write Protect(GNDまたは無接続)
NC:No Connect無接続

接続の回路











以下がAT-Tiny26Lを使ったATMEL24C256のテストプログラム
AT-Tiny26Lは内部発振または外部発振の8MHzで64msの遅延Start-up timeに設定しました
指定したアドレスに値をByteWriteモードで書き込み,
今度はこのアドレスの値をRandomReadモードで読み込み,
値をPAに出力する(LEDが点灯する)
#include <avr/io.h>
#include <avr/interrupt.h>

uint16_t a;//aは16ビット整数でマイクロ秒単位のカウントを行うa<65535

ISR (TIMER0_OVF0_vect){ //タイマカウンタ0のオーバーフローで呼ばれる
a+=256;
}
//マイクロ秒単位の待ち
void delay(int16_t t){
TCNT0=0;
a=0;
while(a+TCNT0<t){}
}

void start(void){
PORTB|= 1<<1;// set PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<1); // clear PB1
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void stop(void){
PORTB&=~(1<<1); // clear PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB|= 1<<1;// set PB1
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void Hclk(void){//SDAがhighで1クロック
PORTB|= 1<<1;// set PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<0); // clear CLK
delay(5);
}
void Lclk(void){//SDAがlowで1クロック
PORTB&=~(1<<1); // clear PB1
PORTB|= 1<<0; // set CLK
delay(5);
PORTB&=~(1<<0); // clear CLK
delay(5);
}

void write(uint16_t add,uint8_t dat){//EEPにデータ書き込み
uint16_t k;
uint8_t i;
start();
for(k=0B100000000;k>0;k=k>>1)if(k & 0B101000000)Hclk();
else Lclk();
i=add/256; //上位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK
i=add%256; //下位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK
i=dat;
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK
stop();//Stop
}

//データ読み込み
uint8_t read(uint16_t add){
uint8_t i;
uint16_t k;
start();
for(k=0B100000000;k>0;k=k>>1)if(k & 0B101000000)Hclk();
else Lclk();
i=add/256; //上位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK

i=add%256; //下位アドレス
for(k=0B10000000;k>0;k=k>>1)if(k & i)Hclk();
else Lclk();
Lclk();//ACK
start();
for(k=0B100000000;k>0;k=k>>1)if(k & 0B101000010)Hclk();
else Lclk();
DDRB&=~(1<<1); //PB1を入力に変更
i=0;
for(k=0B10000000;k>0;k=k>>1){
PORTB|= 1<<0; // set CLK
delay(5);
if(PINB & (1<<1))i+=k;
PORTB&=~(1<<0); // clear CLK
delay(5);
}
DDRB|= 1<<1; //PB1を出力に変更
Hclk();//NO ACK
stop();//Stop
return i;
}

int main(void)
{
uint8_t i,j,x;
DDRA=0B11111111; //PAは全てOUT,LEDを接続しデータを確認する
DDRB=0B00000011; //PB0,PB1:OUT
TCCR0=0B00000010; //タイマカウンタ0の分周をセットCK/8
TIMSK|= 1<<TOIE0; //タイマカウンタ0のオーバーフロー割り込みを許可する
sei(); //All Int Enable 割り込みを可能にする
for(i=0;i<65;i++){
write(i,i); //書き込み最初の引数は16ビット(uint16_t)
delay(30000); //書き込みに10-20msほど必要
x=read(i); //読み込み
PORTA=x; //データをPAに出力
for(j=0;j<50;j++)delay(10000); //Wait 0.5s
}
while(1){}//while(1)終了の無限ループ
}

2008年7月10日木曜日

AD変換の結果を液晶に表示する


(Tiny26でAD変換の結果を液晶に表示する)
以下はTiny26でAD変換の結果を液晶SC1602BSで表示するサンプルプログラムです.
内部発振または外部発振の8MHzに対応しています.
AD変換の入力ピンはADC9です.

AD変換の精度を確保するためにはADPS0,ADPS1,ADPS2で
A/D変換クロックを50-200kHzに設定する必要があります.
;DB7<->PA7 DB6<->PA6
;DB5<->PA5 DB4<->PA4
;DB3<->Open DB2<->Open
;DB1<->Open DB0<->Open
;E<->PA2 R/W<->GND
;RS<->PA0 Vo<->GND
;Vss<->GND Vdd<->5V

以下がソースプログラム
プログラムを修正すれば,温度センサLM35による温度表示
が可能です
/*
Tiny26でAD変換を行い液晶SC1602BSに表示する
TCNT0を使い時間をコントロールする
AD変換の基準電圧は2.56V
AD変換の精度を確保するためにはADPS0-2で
A/D変換クロックを50-200kHzに設定する必要あり
CK=8MHzのときCK/64=125kHz
Int.RC Osc. 8MHz:Start-up time:6CK+64ms
Tiny26--SC1602BSのピン接続
PA0--RS
PA1
PA2--E
PA3
GND
AVCC
PA4--DB4
PA5--DB5
PA6--DB6
PA7--DB7
GND--R/W,Vo,Vss
5V--Vdd
*/

#include <avr/io.h>

//マイクロ秒単位の待ち
void delay(uint8_t t){
TCCR0=0B00000010;//clk/8 (8分周)
TCNT0=255-t;
TIFR |= 1<<TOV0;//TOV0クリア
while(!(TIFR & (1<<TOV0))){}//カウンタがオーバーフローするまで待つ
}

void en(void){
PORTA|= 1<<2;
delay(20);
PORTA&=~(1<<2);
delay(20);
}

void cmnd(uint8_t i){
PORTA=i&(0B11110000);
en();
PORTA=(i<<4)&(0B11110000);//i<<4は4ビット左シフト &はビット間のAND
en();
delay(40);
}

//データの書き込み
void dat(uint8_t i){
PORTA=i&(0B11110000);
PORTA|= 1<<0; //RS:High
en();
PORTA=(i<<4)&(0B11110000);
PORTA|= 1<<0; //RS:High
en();
delay(40);
PORTA&=~(1<<0); //RS clear
}

int main(void)
{
uint8_t ad;
uint16_t i;
DDRA=0xFF; //PA全て出力
DDRB=0X00; //PB全て入力
ADMUX=0B10101001; //INTENAL V REF. LEFT ADJUST, ADC9
ADCSR=0B10000110; //SET ADCSR CK/64

for(i=0;i<80;i++)delay(250); //Wait 20ms
PORTA=0B00110000;
en();
for(i=0;i<20;i++)delay(250); //Wait 5ms
en();
delay(100); //Wait 100Us
en();
delay(50); //Wait 50Us
PORTA=0B00100000; //Function set
en();
delay(50); //Wait 50Us
cmnd(0B00101100); //N=1:2lines F=1:10dots
cmnd(0B00001100); //Display On Off Control
cmnd(0B00000110); //Entry Mode Increment

while(1){
cmnd(0B00000001); //Clear LCD
for(i=0;i<8;i++)delay(250); //Wait 2ms
ADCSR|= 1<<ADSC; //AD start
while(!(ADCSR & (1<<ADIF))){}//ADCSRのビット4(ADIF)が1になるまで待つ
ad=ADCH;
dat(0X30+ad/100); //0X30='0'
ad%=100;
dat(0X30+ad/10);
//dat(0X2E); //dot
ad%=10;
dat(0X30+ad);
//dat(0X20); //SPACE
//dat(0X56); //V
for(i=0;i<4000;i++)delay(250); //Wait 1000ms
}//while(1)
}


以下はアセンブラのプログラムです
;adlc.asm AD conversion +LCDの改良版 2006-9-20
;ADC9(PB6)の電圧をAD変換し10進数に変換し液晶で表示する
;ATtiny26L (Internal Osc 1MHz)
;INTENAL V REF. LEFT ADJUST, ADC9(PB6)
;「R/W RS DB0-7」の信号線にはプルアップ抵抗が入っているので
;DB0-3はGNDにつながずオープン
;マイコンからは書き込みだけで読み込みが無ければ,R/WピンをGNDに
;RS=1:Instruction RS=0:DATA
;Vo (Contrast ADJ)は,電圧がGNDに近づくほど濃くなる.


;LCDのピンは上部左上から
;14:DB7 13:DB6
;12:DB5 11:DB4
;10:DB3 9:DB2
;8:DB1 7:DB0
;6:E(Enable Signal) 5:R/W (Read/Write)
;4:RS(Register Select) 3:Vo (Contrast ADJ)
;2:Vss(0V) 1:Vdd(5V)

;DB7<->PA7 DB6<->PA6
;DB5<->PA5 DB4<->PA4
;DB3<->Open DB2<->Open
;DB1<->Open DB0<->Open
;E<->PA2 R/W<->GND
;RS<->PA0 Vo<->GND
;Vss<->GND Vdd<->5V

.INCLUDE "tn26def.inc"
.equ en =2 ;PB2
.def TEMP =R16
.def data =R17 ;DATA
.def CT1 =R18
.def CT2 =R19
.def CT3 =R20
.def BIN0 =R21
.def DEC0 =R22
.def DEC1 =R23
.def DEC2 =R24
.def DECI =R25
RJMP RESET
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RESET: LDI TEMP,RAMEND
out SP,TEMP
LDI TEMP,0B11110101
out DDRA,TEMP ;PA7-4,PA2,PA0 OUTPUT
;***************** LCD initialize ******************************************
RCALL TM10 ;WAIT ABOUT 10ms
RCALL TM10
RCALL TM10
RCALL TM10
LDI data,0B00110000 ;Function Set DL=1:8bit
OUT PORTA,data
sbi PORTA,en
cbi PORTA,en
RCALL TM10
sbi PORTA,en
cbi PORTA,en
RCALL TM10
sbi PORTA,en
cbi PORTA,en
RCALL TM10
RCALL TM10
LDI data,0B00100000 ;Function Set DL=0:4bit
OUT PORTA,data
sbi PORTA,en
cbi PORTA,en
;ここから4ビットモード
LDI data,0B00101100 ;N=1:2lines F=1:10dots
RCALL cmnd
LDI data,0B00000001 ;Clear Dsplay
RCALL cmnd
LDI data,0B00001100 ;Display On Off Control
RCALL cmnd
LDI data,0B00000110 ;Entry Mode Increment
RCALL cmnd
;*****************AD Initialize**********************************************
LDI TEMP,0X00
OUT DDRB,TEMP ;set PORTB INPUT
LDI TEMP,0B10101001 ;INTENAL V REF. LEFT ADJUST, ADC9
OUT ADMUX,TEMP
LDI TEMP,0B10000000 ;SET ADCSR
OUT ADCSR,TEMP
main: LDI TEMP,0B11000000 ;START CONVERSION
OUT ADCSR,TEMP
ADLP: SBIS ADCSR,4
RJMP ADLP
IN DECI,ADCL ;小数点以下の値
IN BIN0,ADCH
ldi data,0B00000001 ;clear LCD
rcall cmnd ;out command
RCALL B2D ;2進数を10進数に変換 BIN0->DEC2,DEC1,DEC0
LDI TEMP,0X30 ;0X30='0'
ADD DEC2,TEMP
ADD DEC1,TEMP
ADD DEC0,TEMP
MOV data,DEC2
RCALL write
MOV data,DEC1
RCALL write
MOV data,DEC0
RCALL write
;************ Display Dicimal Fraction
LDI data,0X2E ;"."
RCALL write
;DECI: 11000000->75 10000000->50 01000000->25 00000000->00
LDI BIN0,75 ;BIN0=75
SBRS DECI,7 ;DECIのビット7が1ならスキップ
SUBI BIN0,50 ;BIN0-=50
SBRS DECI,6 ;DECIのビット6が1ならスキップ
SUBI BIN0,25 ;BIN0-=25
RCALL B2D ;2進数を10進数に変換 BIN0->DEC2,DEC1,DEC0
LDI TEMP,0X30 ;0X30='0'
ADD DEC1,TEMP
ADD DEC0,TEMP
MOV data,DEC1 ;小数点以下一位
RCALL write
MOV data,DEC0 ;小数点以下二位
RCALL write
;
RCALL TM ;about 0.5s
rjmp main
;*** command and write to LCD *** rs PA0 en PA2
cmnd: MOV TEMP,data ;DATA をTEMPにコピー
SWAP TEMP ;SWAP:上位,下位ビットを交換
CBR data,0B00001111 ;PA0-3->0 RS=0 INSTRUCTION
CBR TEMP,0B00001111
OUT PORTA,data ;上位ビットを書き出し
sbi PORTA,en
cbi PORTA,en
OUT PORTA,TEMP ;下位ビットを書き出し
sbi PORTA,en
cbi PORTA,en
RCALL TM10
RET

write: MOV TEMP,data ;DATA をTEMPにコピー
SWAP TEMP ;SWAP:上位,下位ビットを交換
CBR data,0B00001111 ;RS=0 INSTRUCTION
CBR TEMP,0B00001111
SBR data,0B00000001 ;PA0-3->0 RS=1 DATA
SBR TEMP,0B00000001
OUT PORTA,data ;上位ビットを書き出し
sbi PORTA,en
cbi PORTA,en
OUT PORTA,TEMP ;下位ビットを書き出し
sbi PORTA,en
cbi PORTA,en
RCALL TM10
RET
;******************* timer *********************************************
TM10: LDI CT2,100 ;ABOUT10ms
LP1: LDI CT1,100
LP2: nop
dec CT1
brne LP2
dec CT2
brne LP1
RET
;******************** timer *******************************************
TM: LDI CT3,10
LP: RCALL TM10
dec CT3
brne LP
RCALL TM10
RET
;******************** BINARY TO DECIMAL****************************
B2D: LDI DEC2,0 ;2進数BIN0を10進数DEC2-0に変換
LDI DEC1,0
LDI DEC0,0
MOV CT1,BIN0
D2: LDI TEMP,100 ;100ずつ引いて,DEC2++
CP CT1,TEMP ;CT1とTEMPを比較
BRCS D1 ;キャリーがセット(CT1-TEMP<0)ならD1へジャンプ
SUBI CT1,100
INC DEC2
RJMP D2
D1: LDI TEMP,10 ;10ずつ引いて,DEC1++
CP CT1,TEMP
BRCS D0
SUBI CT1,10
INC DEC1
RJMP D1
D0: LDI TEMP,1 ;1ずつ引いて,DEC0++
CP CT1,TEMP
BRCS DD
SUBI CT1,1
INC DEC0
RJMP D0
DD: RET

(Tiny861でAD変換の結果を液晶に表示する)
以下はTiny861でAD変換の結果を液晶で表示するサンプルプログラムです.
内部発振または外部発振の8MHzに対応しています.
SC1602BS,DMC16117Aで動作確認しています.
AD変換の入力ピンはADC9です.
内部発振Int.RC Osc. 8MHz:Start-up time:6CK+64ms
Tiny861--液晶のピン接続
PA0--DB4
PA1--DB5
PA2--DB6
PA3--DB7
GND
AVCC
PA4--RS
PA5--E
PA6
PA7
GND--R/W,Vo,Vss
VCC--Vdd
PB6--AD入力
DB,RS,Eのピンを変えたときには,#defineを書き換えてください.

#include <avr/io.h>
#define RS 4
#define EN 5
#define DB4 0
#define DB5 1
#define DB6 2
#define DB7 3

//マイクロ秒単位の待ち
void delay(uint8_t t){
TCCR0B=0B00000010;//clk/8
TCNT0L=255-t;
TIFR |= 1<<TOV0;//TOV0クリア
//カウンタがオーバーフローするまで待つ
while(!(TIFR & (1<<TOV0))){}
}

void en(void){
PORTA|= 1<<EN;
delay(20);
PORTA&=~(1<<EN);
delay(20);
}

void cmnd(uint8_t i){
PORTA=(i>>4)&0B00001111;//上位データ
en();
PORTA=i&(0B00001111);//下位データ 
en();
delay(40);
}

//データの書き込み
void dat(uint8_t i){
PORTA=(i>>4)&0B00001111;//上位データ
PORTA|= 1<<RS; //RS
en();
PORTA=i&(0B00001111);//下位データ 
PORTA|= 1<<RS; //RS
en();
delay(40);
PORTA&=~(1<<RS); //RS clear
}

int main(void)
{
uint8_t ad;
uint16_t i;
DDRA=0B11111111; //PA全て出力
DDRB=0X00; //PB全て入力
ADMUX=0B10101001; //VREF2.56V LEFT ADJUST, ADC9
ADCSRA=0B10000110; //SET ADCSR CK/64
ADCSRB=0B00010000; //REFS2:HIGH VREF=2.56V

for(i=0;i<80;i++)delay(250); //Wait 20ms
PORTA=(1<<DB4)|(1<<DB5); //0011
en();
for(i=0;i<20;i++)delay(250); //Wait 5ms
en();
delay(100); //Wait 100Us
en();
delay(50); //Wait 50Us
PORTA=(1<<DB5); //0010
en();
delay(50); //Wait 50Us
cmnd(0B00101100); //N=1:2lines,F=1:10dots
cmnd(0B00001100); //Display On Off Control
cmnd(0B00000110); //Entry Mode Increment

while(1){
cmnd(0B00000001); //Clear LCD
for(i=0;i<8;i++)delay(250); //Wait 2ms
ADCSRA|= 1<<ADSC; //AD start
while(!(ADCSRA & (1<<ADIF))){}//AD変換を待つ
//10進に変換して表示
ad=ADCH;
dat(0X30+ad/100); //0X30='0'
dat('.'); //dot
ad%=100;
dat(0X30+ad/10);
ad%=10;
dat(0X30+ad);
dat(' '); //SPACE
dat('V'); //V
for(i=0;i<4000;i++)delay(250); //Wait 1000ms
}//while(1)
}



AT-Tiny26で液晶SC1602BSの表示を行う


以下はAT-Tiny26Lで液晶SC1602BSの表示を行うプログラムです
SC1602BSとTiny26は次のように接続してください.
;DB7<->PA7 DB6<->PA6
;DB5<->PA5 DB4<->PA4
;DB3<->Open DB2<->Open
;DB1<->Open DB0<->Open
;E<->PA2 R/W<->GND
;RS<->PA0 Vo<->GND
;Vss<->GND Vdd<->5V
クロックは,8MHz(内部発振でも外部発振でも可)で動くように作ってあります.
ちゃんと動けば
Hello
Tiny26
と表示されるはずです.
例えば「H」と表示する場合,dat(0x48);のように16進数を送っても構いませんが
dat('H');のように文字コードを送るのが簡単でよいでしょう.
ちなみに以前は割り込みで時間をコントロールするプログラムを公開していましたが
avr-gccの最近のバージョンでは動かなくなりました.

以下がソースプログラム
/*
液晶SC1602BSの表示を行う
wait()では,タイマ0を使い待ち時間をコントロールする
内部発振Int.RC Osc. 8MHz:Start-up time:6CK+64ms
Tiny26--SC1602BSのピン接続
PA0--RS
PA1
PA2--E
PA3
GND
AVCC
PA4--DB4
PA5--DB5
PA6--DB6
PA7--DB7
GND--R/W,Vo,Vss
5V--Vdd
*/
#include <avr/io.h>

//マイクロ秒単位の待ち
void delay(uint8_t t){
TCCR0=0B00000010;//clk/8 (64分周)
TCNT0=255-t;
TIFR |= 1<<TOV0;//TOV0クリア
while(!(TIFR & (1<<TOV0))){}//カウンタがオーバーフローするまで待つ
}

void en(void){
PORTA|= 1<<2;
delay(20);
PORTA&=~(1<<2);
delay(20);
}

void cmnd(uint8_t i){
PORTA=i&(0B11110000);
en();
PORTA=(i<<4)&(0B11110000); //i<<4は4ビット左シフト &はビット間のAND
en();
delay(40);
}

//データの書き込み
void dat(uint8_t i){
PORTA=i&(0B11110000);
PORTA|= 1<<0; //RS
en();
PORTA=(i<<4)&(0B11110000);
PORTA|= 1<<0; //RS
en();
delay(40);
PORTA&=~(1<<0); //RS clear
}

int main( void )
{
uint16_t i;
DDRA = 0B11111111; //PA all output
TCCR0=0B00000010; //CK/8
TIMSK|= 1<<TOIE0; //T0 Ovf Int Enable
//液晶初期設定
for(i=0;i<80;i++)delay(250); //Wait 20ms
PORTA=0B00110000;
en();
for(i=0;i<20;i++)delay(250); //Wait 5ms
en();
delay(100); //Wait 100Us
en();
delay(50); //Wait 50Us
PORTA=0B00100000; //Function set
en();
delay(50); //Wait 50Us
cmnd(0B00101100); //N=1:2lines F=1:10dots
cmnd(0B00001100); //Display On Off Control
cmnd(0B00000110); //Entry Mode Increment

//液晶初期設定終了

while(1){
cmnd(0B00000001); //Clear LCD
for(i=0;i<8;i++)delay(250); //Wait 2ms
dat('H'); //H
dat('e'); //e
dat('l'); //l
dat('l'); //l
dat('o'); //o
dat(' '); //
dat('T'); //T
dat('i'); //i
dat('n'); //n
dat('y'); //y
dat('2'); //2
dat('6'); //6
for(i=0;i<2000;i++)delay(250); //Wait 500ms
cmnd(0B00000001); //Clear LCD
for(i=0;i<8;i++)delay(250); //Wait 2ms
cmnd(0XC0); //DDRAM=40 Lower Line
dat('コ'); //コ
dat('ン'); //ン
dat('ニ'); //ニ
dat('チ'); //チ
dat('ハ'); //ハ
dat('!'); //!
for(i=0;i<2000;i++)delay(250); //Wait
}//while(1)

}

2008年7月9日水曜日

CygwinやMinGWでCプログラミングを行う

WindowsパソコンにフリーのCygwinまたはMinGWをインストールして,Cプログラミングを行う方法について説明します.

Cygwinとは
Cygwinは一言でいうとWindows上で使えるフリーなUNIXです.Cコンパイラ(gcc)が使えます.ただしgccを使うためには,インストールの際注意が必要です.
インストールには,約100Mのディスクサイズが必要です.

MinGWとは

MinGWは"Minimalist GNU for Windows"の略で,Windows上で最小限の構成でGNUのソフト(gcc)を使えます.コマンドプロンプトからコンパイルおよび実行ができます.
インストールには,約50Mのディスクサイズが必要です.

Cygwinインストールの手順
Cygwinのサイトを開き,
http://cygwin.com/
Install Cygwin nowを選択します.あとは指示に従っていけばよいのですが,SerectPackagesの画面で,DevelDefaultをクリックし,リストの中のgcc-core:C compilerをクリックしてください.そうしないとgccがインストールされません.













CygwinでCプログラミングの手順

Cygwinを起動します.(CygwinのアイコンをWクリックする.)
ユーザのホームディレクトリがどこにあるか確認しましょう.通常は"C:\cygwin\home\ユーザ名"あたりがホームディレクトリになっているはずです.
terapadなどでCのソースファイルを編集しこのディレクトリ内に保存します.
gcc test.c
のようにタイプすればコンパイルできます.バグがある場合には,エラーメッセージが出ます.コンパイルが成功すれば,a.exeというファイルができます.
./a.exe
とタイプすればプログラムが実行できます.
./の意味は,現在のディレクトリの中にあるという意味です.
exitとタイプするとCygwinが終了します.

MinGWインストールの手順
sourceforgeのサイトでmingwでsoftwareをサーチ(Search)すればDownloadMinGWにたどり着けるはずです.
http://sourceforge.net/
この中で"Automated MinGW Installer"を選びDownloadしましょう.あとは指示に従っていけば完了です.Cだけでよいのなら,余計なパッケージは追加する必要はありません.

MinGWでCプログラミングの手順
MinGWのディレクトリの中のbinの中にgccが入っていることを確認しましょう.
terapadなどでCのソースファイルを編集し適当な作業用ディレクトリに保存します.
アクセサリ→コマンドプロンプトを開きます.
cdコマンドで,作業用ディレクトリに移動します.
path c:\MinGW\bin
のようにタイプして,gccが入っているディレクトリにパスを通します.
gcc test.c
のようにタイプすればコンパイルできます.コンパイルが成功すれば,a.exeというファイルができます.
a.exe
とタイプすればプログラムが実行できます.
これらの手順をバッチファイルに保存しておけばダブルクリックですぐにプログラムがコンパイル・実行できます.

2010年2月追記
MinGWをWindows7にインストールしましたが問題なく使えます.
Cのプログラミングだけならg++など追加のインストールは不要でベースパッケージだけで動きます.

2008年7月8日火曜日

ATMELのATTiny26入門


ワンチップマイコンは,計算処理を行うALU(演算論理装置),メモリ,プログラムを格納するROM,IOポートなどが一つのチップに納められています.
最近では,フラッシュROM内蔵のものが多く出回っておりプログラムが簡単に何度でも書き換え可能なので,初心者へのしきいが低くなっています.
アナログコンパレータやADコンバータを持ったものもあり,色々な応用が考えられます.
プログラムメモリやデータメモリとは別に,EEPROMを持っているものが多くあります.EEPROMはデータが不揮発性ですから,あらかじめ必要なデータを入れておいたり,実行中に得られたデータを保存しておいたりする利用が考えられます.
消費電力は非常に小さく,LEDを光らせるような簡単なものなら,充電電池だけでも丸1日以上連続動作が可能です.
ワンチップマイコンを使うためには,マイコンのデータシートを入手し,構成図を見て全体の概要を理解する必要があります.特に汎用レジスタ,プログラムメモリ,データメモリ(RAM),IOレジスタ, EEPROMなどについて理解する必要があります.
ATMEL社のAT-Tiny26は,ADコンバータ付き,EEPROM付き,最大16MHz動作などの特徴を備えた優れものです.クロックも外部発振と内蔵発振器を選べます.
以下に,AVRISPmkIIを使ったTiny26の利用法について説明します.

AT-Tiny26は,最大16MHz動作のATTINY26L-16Pと最大8MHz動作のATTINY26L-8Pの2種類があります.
必要なもの
Windowsパソコン,AVRISPmkII,AT-Tiny26
パソコンに必要なソフトをインストールする.
AVRStudioとサービスパックをインストールする.これらはAVRISPmkIIのパッケージにCDとして付いています.ATMELのページからもダウンロードできます.アセンブラのプログラミングだけでよいのならこれで十分です.
C言語でプログラミングしたい場合には,フリーソフトのWINAVRをインストールします.

プログラムの編集とビルド
AVRStudioを起動し,マイコンをTiny26に選びプロジェクトを開きます.アセンブラまたはソースファイルのプログラムを編集し,ビルドします.ビルドが成功すると○.hexというファイルができます.

プログラムの書き込み手順
AVRISP-mkIIをPCに接続します.
AVRISP-mkIIのISP端子とマイコンのSCK,MISO,MOSIなどを接続し,マイコンの回路に電源を入れる.
ProgramAVRのメニューから,マイコンとの接続を確認し,Flash>Programで○.hexをマイコンに書き込みます.

FUSEの設定
FUSEの設定で,マイコンの発振周波数の設定や内部発振と外部発振の切り替えを設定します.

AVRISP-mkIIのISP端子とマイコンとの接続をはずし,実行したい回路にマイコンをセットし,電源を入れれば,マイコンが動くはずです.

簡単なアセンブラプログラムの例
PA0,PA1,PA2,PA3に接続したLEDを順次点灯させる.
.INCLUDE &quot;tn26def.inc&quot;
RJMP RESET
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RETI
RESET: LDI R16,RAMEND
OUT SP,R16
LDI R16,0XFF
OUT DDRA,R16 ;PA=OUTPUT
MAIN: LDI R21,0X00
OUT PORTA,R21
RCALL TIME
LDI R21,0X01
OUT PORTA,R21
RCALL TIME
LDI R21,0X02
OUT PORTA,R21
RCALL TIME
LDI R21,0X04
OUT PORTA,R21
RCALL TIME
LDI R21,0X08
OUT PORTA,R21
RCALL TIME
RJMP MAIN
TIME: LDI R24,5
LOOP1: LDI R23,100 ;1ms x 100=100ms
LOOP2: LDI R22,100 ;1000ns x 10 cycle x 100=1ms
LOOP3: NOP
NOP
NOP
NOP
NOP
NOP
NOP
DEC R22
BRNE LOOP3
DEC R23
BRNE LOOP2
DEC R24
BRNE LOOP1
RET

簡単なCプログラムの例
PA0に接続したLEDを点滅させる.

#include <avr/io.h>

void wait(int x);

int main( void )
{
DDRA=0xff; /* PortAをすべて出力に設定する */

for (;;) { /* 無限ループ */
PORTA=0x00; /* PA0 off */
wait(80); /* wait関数(waitルーチン)実行 */
PORTA=0x01; /* PA0 on */
wait(80);
}
}

/* ********** サブルーチン ******************* */
void wait(int x){ /* 時間稼ぎ */
int i,j;
for(i=1;i<x;i++){
for(j=1;j<20000;j++){
j=j; /* dummy */
}
}
}


その他
EEPROMデータの読み込み
EEPROM>ReadでマイコンのEEPROMデータを読み込みファイルに保存することができます.

ATtiny26の最近の状況(2008-10-15追記)
秋月ではATtiny26の扱いをやめました.何故?一方で秋月ではAVRISPmkIIを4000円で売り出しています.今から始める人は,AVRISPmkIIを使ったプログラミングがおすすめだと思います.
ATMELではATtiny26の上位チップとしてATtiny261,ATtiny461,ATtiny861を出しています.
AVRISPmkIIを使ってATtiny861は書き込みができました.C言語のプログラムでしたら
ATtiny26ののものが,ほとんどそのまま使えるはずです.

2008年7月7日月曜日

サウンドファイルwavを開く


以下はwav形式のサウンドファイルをC言語で開くプログラムです
時間とデータを出力します.
リニアPCM形式16ビットのモノラルデータに対応しています.
//wav形式データを読み込むプログラム
//リニアPCM形式のデータなら先頭の44バイトがヘッダである
//16ビットモノラルデータに対応
//時間とデータを出力する
//ヘッダは下位データから並ぶ正数
//波形データは下位データから並ぶ符号付整数
//0Xは16進数,0Bは2進数
#include <stdio.h>
main(){
int i,a,b,rate;
FILE *fin=fopen("test.wav","rb");
for(i=0;i&lt;20; i++)b=fgetc(fin);//RIFF to "fmt ""byte"
for(i=0;i&lt;2; i++)b=fgetc(fin);//format ID
b=fgetc(fin);
b+=fgetc(fin)*0X100;
printf("ch=%d\n",b);
b=fgetc(fin);
b+=fgetc(fin)*0X100;
b+=fgetc(fin)*0X10000;
b+=fgetc(fin)*0X1000000;
printf("rate=%d\n",b);
rate=b;//サンプリングレート(sample/s)
b=fgetc(fin);
b+=fgetc(fin)*0X100;
b+=fgetc(fin)*0X10000;
b+=fgetc(fin)*0X1000000;
printf("Byte/sec=%d\n",b);
for(i=0;i&lt;2; i++)b=fgetc(fin);//Block size
//bit/sample
b=fgetc(fin);
b+=fgetc(fin)*0X100;
if(b!=16){printf("data is not 16bit"); return 0;}
for(i=0;i&lt;4; i++)b=fgetc(fin);//data
b=fgetc(fin);
b+=fgetc(fin)*0X100;
b+=fgetc(fin)*0X10000;
b+=fgetc(fin)*0X1000000;
printf("total data=%d\n",b);
for(i=0;i&lt;1000;i++){
a=fgetc(fin);//下位バイト
b=fgetc(fin);//上位バイト
a+=(b%0X80)*0X100;
if(b&amp;gt;=0X80)a-=0X8000;
printf("%f\t%d\n",(float)i/rate,a);
}
fclose(fin);
}

2008年6月23日月曜日

Visual C++ Express Editionを用いたプログラミングメモ

以下は軽量で最低限の機能(授業で使う基本的なC言語のプログラミングなど)を使う例です.
Visual C++ はC:\Program Filesに,C言語のプログラムはc:\testに作成するものとする.使用したパソコンはWindowsXP.
MicrosoftのVisual C++ Express Edition のサイトにアクセスする.Visual C++ Express Edition をWeb インストール (ダウンロード)する.このソフトを実行しますかと聞かれたら,実行をクリックする.
ライセンスに同意する.
インストールオプションのチェックボックスを全部空にする.
Visual C++ Express Editionがインストールされる.

実行方法
すべてのプログラム>>アクセサリ>>コマンドプロンプト
でコマンドプロンプトの窓を開く.
cdコマンドでC言語のソースプログラムが入っているフォルダに移動する.
Visual C++ Express Editionをインストールしたディレクトリの下のVCフォルダに含まれているバッチファイルの「vcvars32.bat」を実行する.
"C:\Program Files\Microsoft Visual Studio 8\VC\bin\vcvars32.bat"
(「vcvars32.bat」のアイコンをコマンドプロンプトにドラックしても良い.)
cl test.cでコンパイルするとtest.exeが作られる.
test.exeをタイプしてプログラムを実行する.

2008年6月18日水曜日

Fedora,Vine,Debian,KnoppixでLinux一本勝負?

KnoppixをUSBにインストールする方法(2010.08.19)
CD版knoppixのisoファイルをダウンロードする.
CDライティングソフトで,knoppixのisoファイルをCDに書き込む.
PCにCDをセットし,立ち上げ前にBIOSの設定を,HDよりCDを優先させると
CD版Knoppixが起動する.
1GBのUSBメモリを接続する.
システムツールメニューにInstall Knoppix to flash diskがあるので,
インストール先にUSBを選択して実行すれば出来上がり.

いくつかのLinaxの比較(2008)
最近のLinuxの状況を知らなかったので,いくつかのLinaxをHDにインストールして,日本語環境,OpenOfficeの使い勝手などを調べました.KnoppixのみCD-ROMから起動します.
使用したマザーボード:AOpen s651m
メモリ:256M

CDの作り方
CDの作り方はみんな同じ.CdManipulatorでCDにisoファイルを焼く.(書き込めばよい)
CdManipulatorをダウンロードし解凍する.インストールしなくてもCdManipulator.exeをダブルクリックで実行できる.









下の左から二番目のCDのマスタリング又はイメージのマスタリングを選択する.
トラックリストを開き,トラック>>isoファイルの挿入を選択し,ダウンロードしたisoファイルを選ぶ.あとはCDへ書き込みボタンを押すだけで,CDが作れる.

いずれも設定など特に問題なく,インストールおよび起動できました.

Debian GNU/Linux 4.0
Debianはそのままでは日本語が使えなかった. 原因究明を断念し,引き続きFedora9を試す.
Fedora9
日本語環境がデフォルトで準備されていない以外は,OpenOfficeなど必要なものが揃っている.
FedoraはインストールCDが3枚になった.
Fedora-9-i386-disc1.iso
Fedora-9-i386-disc2.iso
Fedora-9-i386-disc3.iso
HDが5Gぐらい埋まった.もはやWindowsに比べて軽いOSというイメージは無い.
そのままでは日本語が使えなかったが比較的簡単に日本語化が可能.
端末を開き,スーパーユーザとなり次の命令を実行する.
yum groupinstall 'Japanese Support' --exclude=xorg-x11-server-Xorg

システムメニュー>>設定>>ユーザ向け>>入力メソッドを選択し
入力メソッド設定ツールを実行する.
入力メソッド設定ツールウィンドウの入力メソッドの機能を有効にするのチェックボックスをチェックし,入力メソッド設定ツールを閉じる.

Vine4.2
Vineはそのまま日本語が使える.
Openofficeは後でインストールする必要がある.Synapticというインストールソフトを使えば良い.

KNOPPIX5.3
OpenOfficeなど最低限必要なものが揃っている.
KNOPPIX5.3(日本語版),CDから簡単にLinuxが使えるのは便利だが,CDドライブが回り続けるのはちょっと不安である.やはりUSBメモリにインストールしたものが良いかもしれない.

2008年6月12日木曜日

Windows版 Mathematicaの使い方
(覚え書き)


Windowsでは式を入力してShiftキーを押しながらEnterキーを押す.(Shift+Enter)

Mathematicaの基礎知識
式を入力すると入力(In)に対する結果(Out)が表示される.

関数は[]でくくる.関数の最初は大文字である.
よく使う定数
E(自然対数の低),PI(円周率),I(虚数),Infinity(無限大)

たとえば x^3 + y^3(Shift+Enter)で式が記憶される.
この式の因数分解を行いたい場合
Factor[%]
ここで%は直前の式をあらわす
今度は,上の結果を展開したい場合
Expand[%]
でもとに戻る.

方程式の解法
Solve[4x-1==0,x]
Solve[a x^2+b x+c==0,x]
(注)Solve[ax^2+bx+c==0,x] とすると正しい結果が出ない.これはMathematicaではaxやbxも変数とみなすためである.異なる変数の間にはスペースが必要.スペースの代わりに*を使っても良い.
Solve[a x^2+b x+c==0,x]
Solve[{x-4y==-2,2x+y==5},{x,y}]
上の問題は逆行列を使った行列演算でも解くことができる.
A={{1,-4},{2,1}}
Y={-2,5}
Inverse[A].Y

関数の極限
Limit[(1+1/x)^x,x->Infinity]
矢印や無限大は,右のリストから選んでもよい

式の微分
x^3-4x^2+Exp[-x^2]+Sin[2x]
D[%, x]

微分方程式
DSolve[{x''[t]+x[t]==0,x[0]==1,x'[0]==0},x[t],t]
DSolve[{x''[t]+x'[t]+x[t]==0,x[0]==1,x'[0]==0},x[t],t]
DSolve[{x''[t]+x'[t]+x[t]==2Cos[t],x[0]==1,x'[0]==0},x[t],t]
Plot[式,{t,a,b}]で結果をグラフ表示することも可能である.a,bは横軸の範囲.

式の積分
1/x-4x^2+Exp[-x]+Sin[2x]
Integrate[%, x]

定積分
Integrate[Exp[-x^2],{x,0,1}]
式の値を数値で与えるには
N[%]
表示桁数を指定することもできる.
N[%,100]
N[]を忘れた場合,とりあえず1.0を式に掛けておいてもよい.
例えばIntegrate[Exp[-x^2],{x,0,1}]*1.0

複素数の絶対値と偏角
z = (1 + I)/Sqrt[2]
Abs[z]
Arg[z]

行列
行列やベクトルの定義
A={{1,-4},{2,1}}
B={{a,b},{c,d}}
C={-2,5}
行列の演算
A.B (内積)
B.B
Cross[A,B] (外積)
A^3
逆行列
Inverse[A]
行列式
Det[A]
行列の固有値と固有ベクトルを求める.
A={{3,-5,-5},{-1,7,5},{1,-9,-7}}
Eigenvalues[A]
Eigenvectors[A]
固有値と固有ベクトルが条件を満たすか確認する
固有値-2に対する固有ベクトルがB = {1, -1, 2}のとき
A.B と -2Bが同じ結果になる
A={{2,0,-1},{0,2,0},{-1,0,2}}

グラフ描画
Plot[Sin[4x] Exp[-x^2],{x,-5,5}]
ParametricPlot[{Sin[5t],Cos[3t]},{t,0,2Pi}]
ParametricPlot3D[{Sin[15t], Cos[15t], t}, {t, 0, 2Pi}, PlotPoints -> 400]
Plot3D[Exp[-(x^2 + y^2)], {x, -5, 5}, {y, -5, 5}]
ContourPlot[Exp[-(x^2 + y^2)], {x, -5, 5}, {y, -5, 5}]
グラフの上下が削られる場合には以下のようにオプションを追加する.
Plot[Sin[10x] Exp[-x^2], {x, -5, 5}, PlotRange -> {-1, 1}]
Plot3D[Exp[-(x^2 + y^2)], {x, -5, 5}, {y, -5, 5}, PlotRange -> All]
Plot3D[Exp[-(x^2 + y^2)], {x, -5, 5}, {y, -5, 5}, PlotRange -> {0,1}]
グラフを細かく(精密に)書きたい場合には,Plotpointsで関数のサンプリング数を変える.
Plot3D[Exp[-(x^2 + y^2)], {x, -5, 5}, {y, -5, 5}, PlotRange -> All,PlotPoints->100]

関数の定義
f[x_]:=x^2 Sin[x]
Plot[f[x],{x,-10,10}]
変数への値の代入
x=10
x^2

最も簡単な形式を返す
FullSimplify[1/(Sqrt[2] - 1)]

値のクリア
Remove["x"]

2008年6月10日火曜日

C言語のプログラミング入門


C言語の学習を行うための自習テキストです.例題を通して配列までの範囲を学習できます.

プログラミングⅠ資料No.1
第1回

今日の課題
簡単なCのプログラムを実行してみる.

例題1
C言語では,スペース,タブ,改行は適当に入れて構いません.プログラムの内部が見やすいよう右にずれていますが,Tabキーを使っています.ずらさなくても,スペースを入れても構いません.コンパイラが全角スペースを認識できませんので,全角スペースを入れるとプログラムが動きません.
#include <stdio.h>
int main(void)
{
printf("ABC");
}
(注)プログラムで全角文字が使えるのは,コメントや文字列のみで,それ以外の場所に全角文字を入れるとエラーがでます.

Cygwinの使い方
CygwinのアイコンをWクリックする.
gcc test1.c とタイプするとa.exeというファイルができる.
バグがある場合には,エラーメッセージが出る.
./a.exeとタイプするとプログラムが動く. (./ の意味は?)
exitとタイプするとCygwinが終了する
Cygwinで最低限知っておいた方が良いコマンド
↑:前の命令を呼び出す
↓:後の命令を呼び出す
ls:ファイルのリストを出す
ls -l:ファイルのリストを詳細情報付で出す
その他知っておいた方がよいコマンド
(mv,cp,rm,pwd,mkdir,cd,cat,vi)
cygwinは一度開いたら授業の間ずっと開きっぱなしで構いません.
このウェブの画面とメモ帳とCygwinを見やすいように並べてください.

例題2
#include <stdio.h>
int main(void)
{
int i,j=0;
for(i=1;i<=10;i++)j=j+i;
printf("%d\n",j);
}

思ったとおりに動かない場合
ファイルを右クリックし「編集」で,プログラムを確認し修正し,上書き保存し,
Cygwinで再度コンパイル>実行

例題3
#include <stdio.h>
#include <math.h>
int main(void)
{
int i;
float x;
for(i=0;i<10;i++){
x=sqrt(1.0*i);
printf("%d %f\n",i,x);}
}

例題4
/*鶴亀算,足は合計26本,全部で10匹*/
#include <stdio.h>
int main(void)
{
int turu,kame,ashi;
for(turu=0;turu<=10;turu++){
kame=(10-turu);
ashi=turu*2+kame*4;
if(ashi == 26)printf("鶴は%d匹 亀は%d匹\n",turu,kame);
}
}

例題5
#include <stdio.h>
int main(void)
{
char a;
a='A';
printf("%c\n",a);
putchar('B');
putchar('B'+5);
printf ("\n");
puts("xyz");
printf ("\n");
puts("abc");
}

本日のミニプログラム
#include <stdio.h>
int main(void)
{
printf("Hello");
}

Unix上のエディタviの使い方
起動  vi [ファイル名 ...]
指定されたファイルが無い場合は新規作成モードとなる.ファイル名にはワイルドカード('*', '?'など)も使用可能.
編集モード タイプした文字がそのまま反映される.Escでコマンドモードへ戻る.
コマンドモード 以下に示す各種コマンドが使える.起動直後はこのモード.(iで編集モードへ)
コマンドモードにおける各種コマンド (各コマンドの前に数字を打つと,その回数分だけ同じ動作を繰り返す)
:q セーブせずに終了
:q! 変更した行もセーブせずに終了
:w セーブするが終了しない.
:wq セーブして終了
編集モードへ移るためのキー
i 現在のカーソル位置から挿入.
R 現在のカーソル位置から置換.
A 現在行の末尾に追加.
O 現在行の前に行挿入.
o 現在行の次に行挿入.
カーソル移動キー h(←) 左  j(↓) 上  k(↑) 下  l(→) 右
0 行頭へ
$ 行末へ
[Enter] 次の行の先頭へ
w 次の単語へ
b 前の単語へ
ctrl + f 次画面へ
ctrl + b 前画面へ
1G 文頭へ
G 文末へ
nnG nn行目へ
変更キー
x 1文字削除
dd 1行削除(カット)
cw(Change Word) 1語変更
c$ カーソル位置から行末まで変更
dw(Delete Word) 1語削除
d$ カーソル位置から行末まで削除
検索 /正規表現 前方検索
?正規表現 後方検索
n 次の候補
N 前の候補
カット/コピー&ペースト
yy 1行コピー
dd 1行カット
p ペースト(張り付け)
その他 .(ドット) 直前の変更操作の繰り返し
u 直前の変更操作の取り消し(何回でも戻れる)
gccのオプション
-Wunused 未使用の変数をチェック
-Wreturn-type 関数の返却値と型の整合性
[例]main関数の終了が exit関数,或いは return文でないと次の warning が出る.
  control reaches end of non-void function
-Wformat printf や scanf の変換指定子のチェック
-Wimplict 関数や引数の暗黙的宣言のチェック
-Wuninitialiezed  変数の初期化チェック(但し,最適化用オプション利用時)
-Wall 上記オプションを含む warning用オプションのすべて.
どのオプションが含まれるかはコンパイラのマニュアルを.
-O 処理の最適化.最適化については,他のオプション -O2 などあり.
どのような最適化が行われるかはコンパイラのマニュアルを.
-o 実行ファイル名を指定.
-c オブジェクトファイルの生成.
-S 翻訳処理のみ.アセンブリ言語ファイルが生成される.

Cygwinのインストール
Cygwinのサイトにアクセスする. http://www.cygwin.com/
Install or update now!をクリックする.
ファイルのダウンロード>実行を選択する.
次へをクリックしてInstall from Internetを選択する.
Choose A Download Siteで日本のサイトFtp://XXXXXXX.jpを選ぶとダウンロードが早い.
このままインストールするとgcc(Cコンパイラ)がインストールされないので,
SelectPackagesの画面でDevelDefaultをクリックする
Packagesのリストの中にgcc-core:C compilerというのがあるのでチェックを入れる.
あとは次へを続けてクリックしていけばCygwinがインストールされてgccが使えるはずです.

プログラミングⅠ資料No.2
今日の課題
文字の表示.コメント.特殊文字の表示.実行結果のファイルへの出力方法.実行結果の送信メールへの取込み方法.

作成ファイルの整理
作成ファイルには以下の例のように,授業の回数や演習の日付で系統的な名前をつけておきましょう.
例 2007年5月11日のプログラム 0511a.c 0511b.c 0511c.c

Cygwin環境のコンパイルと実行
「UNIX環境ではfccを使いコンパイルする」と書かれているものもありますが,Cygwin環境ではgccを使います.プログラムの実行は ./a.exe です.(./は「今見ているフォルダの中からa.exeを探しなさい」という意味があります)


例題
/* 学籍番号xxxx 氏名xxxx 日付xxxx
例題 */

#include <stdio.h>
int main(void)
{
printf("学籍番号\t氏名(読み)\n");
printf("xx年xx月xx日\n");
printf("今日はよい天気です\n");
}
プログラムは,半角スペースやTabを使って見やすいようにずらしてください.(インデント)

コメント
プログラム中で/*から始まって*/で終わるまでの部分は,実行されません.(コメント)
またC++では文の//より右側はコメントとして実行されません.
(注意)全角文字はコメント(/*...*/)文字列("...")以外では使えません.まず全角以外の編集をしてから,最後に全角の部分を追加するのが良いでしょう.

拡張表記
printf("")でダブルクオーテーションに囲まれた中に,以下の記述を入れると,改行などの表示が出来ます.
\n:改行 \t:タブ \\:「\」円記号
\0:ヌル文字 \":「"」ダブルクォテーション

注 ヌル文字は,文字列の終端であることを表す特殊な文字で表示されません.
\n や\tのように/の逆の文字(バックスラッシュ)を使っている本もありますが,実際には半角の円記号"\"を使ってください.

実行結果のファイルへの出力方法と表示のコピー
リダイレクトを使うと結果が画面に表示されるのではなく,新しいファイルができ書き込まれます.
./a.exe>output.txt
.txtはテキストファイルを示す拡張子です.
ウィンドウズ上で,作成したテキストファイルをワードパッドで開くことができます.
またCygwinでは上のバーを右クリックし編集>範囲指定で範囲を指定し,編集>コピーで,指定した範囲をコピーし,ワードやメールに貼り付けることができます.

練習問題1 例題で文字列から\nを除いて実行してみましょう.
練習問題2 例題でprintf文の最後のセミコロン;を除いて実行してみましょう.
練習問題3 例題を次のように書き換え実行してみましょう.
#include <stdio.h>
int main(void)
{
printf
("hello , world ");
}

練習問題4例題と以下のプログラムの実行結果を比較しましょう.
#include <stdio.h>
int main(void)
{
printf("hello");
printf(" , ");
printf("\n");
}
printf()で表示を行う場合には,最後に改行\nを入れましょう.

例題
#include <stdio.h>
int main(void)
{
printf("C言語では\"\\n\"は改行をあらわします\n");
printf("\\nがないと");
printf("改行されません");
}

Cygwinでは日本語を表示する場合に,文字が化けることがあります.

本日のミニプログラム

#include <stdio.h>
int main(void)
{
printf("\tTab"); /*Tab*/
}


プログラミングⅠ資料No.3
今日の課題
変数,値の型,データの表示printf(),データの読み込みscanf()

例題3-1
#include <stdio.h>

int main(void)
{
int math,phys,wa,heikin;
math=60;
phys=70;

wa=math+phys;
heikin=wa/2;

printf("点数の和=%d\n",wa);
printf("平均=%d\n",heikin);
}

math,phys,wa,heikinは整数型の変数です.変数の名前は自分で決められます.
math=60;は変数mathに60を代入します.
wa=math+phys;は変数mathの値と変数physの値を足した値を変数waに代入します.
heikin=wa/2;は変数waの値を2で割った値を変数heikinに代入します.

変数の定義と型について
C言語では,プログラムの中で値を保存するのに変数を使います.最初に使いたい変数がどのような型(整数,実数など)なのかを決めておく必要があります.これを変数の定義と呼びます.

int i,j; /*iとj は整数型の変数である*/
double x; /*xは倍精度の実数である */
変数の定義と同時に,変数に値を代入することもできます.
int k=3; /*kは整数型の変数である またkに3を代入する*/
通常i,j,k,m,nなどの変数は,整数型の変数として使うことが多いです.
C言語では,次のような変数が使えます.
整数型 int 実数型 float 倍精度実数型 double 文字型 char

関数printf()について
この関数を用いて標準出力(画面)にデータ(文字列)を表示できます.
%dは整数型の書式指定子と呼ばれ,後に続く整数の値が%dに置き換わって表示されます.

練習問題3-1
例題3-1で赤字の部分を1行に置き換えることができます.
heikin=(wa=(math=60)+(phys=70))/2;
実行して,命令の意味について考えてみよう.

練習問題3-2
例題3-1で変数の定義と,変数への値の代入を同時に行うプログラムに書き替えよう.

例題3-2
#include <stdio.h>

int main(void)
{
int a,b,c;
printf("Aの点数?\n");
scanf("%d",&a);
printf("Bの点数?\n");
scanf("%d",&b);
printf("Cの点数?\n");
scanf("%d",&c);

printf("A,B,Cの和=%d\n",a+b+c);
printf("平均=%d\n",(a+b+c)/3);
}
3,4,5と入力した場合と3,4,4と入力した場合の結果を比べてみましょう.

関数scanf()について
scanf("%d",&x)の文では,整数型の書式指定子%dが使われているので,キーボードに入力した値が,整数として変数xに代入されます.
scanf("%f",&a)では,実数型の書式指定子%fが使われているので,キーボードに入力した値が,実数として変数aに代入されます.
実数型でprintf(),scanf()を使う場合には,%dではなく,%fになることに注意しましょう.
scanf()では変数の先頭に&(アンパッサンド)が付くことに気をつけてください.また変数の型と書式指定子%○が一致しなくてはいけません.

例題3-3
#include <stdio.h>
int main(void)
{
float a,b,h,s;
printf("台形の面積を求めます\n");
printf("上底?\n");
scanf("%f",&a);
printf("下底?\n");
scanf("%f",&b);
printf("高さ?\n");
scanf("%f",&h);
s=(a+b)*h/2.0;
printf("台形の面積=%f\n",s);
}
実数の表示について
実数型の書式指定子は%fなのでprintf("台形の面積=%f\n",s);のように,実数を表示することができます.
printf()を使って実数型の値を表示する場合,printf("%10.5f\n",x); のように全桁数(ピリオドの前の数)と小数点以下の桁数(ピリオドの後の数)を指定することができます.桁の指定は%とfにはさまれる事に注意しましょう.指定しないで%fとした場合には,デフォルト(コンパイラで定義された状態)で表示されます.
大文字と小文字について
プログラムの中で同じアルファベットでも大文字と小文字は別の文字として扱われます.またscanf,include,intなどのC言語で決められた単語で大文字と小文字を間違えるとエラーになります.
次のプログラムでは変数aとAが別の変数として扱われています.
#include <stdio.h>
int main(void)
{
int a,A;
scanf("%d",&a);
A=a*3;
printf("%d",A);
}

演算による結果
整数同士,実数同士の演算では,結果の値の型は変わりません.整数と実数が混ざった演算では,結果の型は実数になります.
また整数同士の乗除では小数点以下が切り捨てられることに注意しましょう.

例題
#include <stdio.h>
int main(void)
{
int a,b,c;
float d;
scanf("%d",&a);
scanf("%d",&b);
c=a+b;
d=c/2.0;
printf("%d\n",c);
printf("%f\n",d);
}

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int x,y; /*変数の定義*/
scanf("%d",&x); /*入力*/
y=x*2; /*代入*/
printf("%d",y); /*出力*/
}
printf()は値を表示することができる.scanf()は,入力した値の格納先を示す.(変数の前に&が必要)


プログラミングⅠ資料No.4
今日の課題
文字型変数,文字の入出力getchar(),putchar(),色々な演算(z=5%2,x*=3,i++など)

整数と実数が混ざった計算の注意
整数と実数が混ざった計算では,頭で考えたとおりの結果にならないことがあるので注意しましょう.例えば
1/9.0=0.1111111
0.1111111*9=0.9999999 この値を整数に変換すると0になる!
次の例でも結果が0になることがあります.
int i;
float x;
x=1/100.0;
i=x*100.0;
printf("%d\n",i);

例題4-1
/*小文字を大文字に変換する */
#include <stdio.h>
int main(void)
{
char moji;
printf("小文字");
scanf("%c",&moji);
moji=moji-'a'+'A';
printf("%c\n",moji);
}

文字とASCIIコード
'a','@','6'のようにシングルクオーテーション(')で囲まれたものは文字(文字型の値)です.文字は計算機の中では数値として扱われるので,足し算や引き算が出来ます.文字と数値との関係を決めた代表的なものに,ASCII(アスキーと呼ぶ)コードがあります.付録に,ASCIIコードを示します.
"abc","文字"のようにダブルクオーテーション(")で囲まれたものは文字列です.'a'と"a"は違うものなので気をつけてください.

文字型の変数と文字の入出力
charは文字型の変数を定義します.文字型の変数は整数として扱うことができます.上の例題で「char」を「int」に変えて実行してみましょう.scanf()やprintf()で文字を入出力する場合には,%cの書式指定子を使います.文字の入出力には,getchar()とputchar()も使えます.

練習問題4-1
例題では文字'b'がどのように文字'B'に変換されるかを考えて,今度は大文字を読み取り小文字に変換するプログラムに修正してください.

練習問題4-2
moji=getchar();で1文字を変数mojiに読み込むことができます.putchar(moji);で変数mojiの値(1文字)を表示することができます.
getchar()とputchar()を使って例題4-1を書きかえてください.

練習問題4-3
次のプログラムを実行しましょう.
(注)for(i=0;i<128;i++)...は変数iの値を0から127まで繰り返すという処理ですが,詳しくは後ほど勉強します.
#include <stdio.h>
int main(void)
{
int i;
for(i=0;i<128;i++){
printf("%d\t",i);
putchar(i);
putchar('\n');
}

}

例題4-2
#include <stdio.h>
int main(void)
{
int x;
float y;
x=10;
x++;
printf("%d\n",x);
x=20;
x%=7;
printf("%d\n",x);
y=8.0;
y/=2.0;
printf("%f\n",y);
y/=2.0;
printf("%f\n",y);
y/=2.0;
printf("%f\n",y);
}

剰余算
%は整数を整数で割ったときの余りを求める演算です.(x=20%6;)書式指定子の%とは違うので気をつけましょう.

自己代入型演算子,インクリメント,デクリメント
変数に演算処理を行いその結果を同じ変数に代入するものを自己代入型演算子とよびます.また変数の値を一つ増やすのがインクリメント,一つ減らすのがデクリメントです.
加 x=x+a; x+=a; (x++ x=x+1;)
減 x=x-a; x-=a; (x-- x=x-1;)
乗 x=x*a; x*=a;
除 x=x/a; x/=a;
剰余 x=x%a; x%=a;

練習問題4-4
例題4-1を自己代入型演算子を使ったプログラムに書き換えましょう.

一歩進んで
インクリメント,デクリメントでは,演算子が変数の前にある書き方(前置型)と,変数の後ろある書き方(後置型)があります.i++ ++i j=i++ j=++i
前置型は全体の式の計算(代入)より先に,インクリメント,デクリメントを行います.

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int x=2;
x+=3;
printf("%d",x);
}


ASCIIコード 10進数と16進数の対応についてもよく理解しておきましょう.
文字 10進 16進 文字 10進 16進 文字 10進 16進 文字 10進 16進
NUL 0 0x00 SP 32 0x20 @ 64 0x40 ` 96 0x60
SOH 1 0x01 ! 33 0x21 A 65 0x41 a 97 0x61
STX 2 0x02 " 34 0x22 B 66 0x42 b 98 0x62
ETX 3 0x03 # 35 0x23 C 67 0x43 c 99 0x63
EOT 4 0x04 $ 36 0x24 D 68 0x44 d 100 0x64
ENQ 5 0x05 % 37 0x25 E 69 0x45 e 101 0x65
ACK 6 0x06 & 38 0x26 F 70 0x46 f 102 0x66
BEL 7 0x07 ' 39 0x27 G 71 0x47 g 103 0x67
BS 8 0x08 ( 40 0x28 H 72 0x48 h 104 0x68
HT 9 0x09 ) 41 0x29 I 73 0x49 i 105 0x69
NL* 10 0x0a * 42 0x2a J 74 0x4a j 106 0x6a
VT 11 0x0b + 43 0x2b K 75 0x4b k 107 0x6b
NP 12 0x0c , 44 0x2c L 76 0x4c l 108 0x6c
CR 13 0x0d - 45 0x2d M 77 0x4d m 109 0x6d
SO 14 0x0e . 46 0x2e N 78 0x4e n 110 0x6e
SI 15 0x0f / 47 0x2f O 79 0x4f o 111 0x6f
DLE 16 0x10 0 48 0x30 P 80 0x50 p 112 0x70
DC1 17 0x11 1 49 0x31 Q 81 0x51 q 113 0x71
DC2 18 0x12 2 50 0x32 R 82 0x52 r 114 0x72
DC3 19 0x13 3 51 0x33 S 83 0x53 s 115 0x73
DC4 20 0x14 4 52 0x34 T 84 0x54 t 116 0x74
NAK 21 0x15 5 53 0x35 U 85 0x55 u 117 0x75
SYN 22 0x16 6 54 0x36 V 86 0x56 v 118 0x76
ETB 23 0x17 7 55 0x37 W 87 0x57 w 119 0x77
CAN 24 0x18 8 56 0x38 X 88 0x58 x 120 0x78
EM 25 0x19 9 57 0x39 Y 89 0x59 y 121 0x79
SUB 26 0x1a : 58 0x3a Z 90 0x5a z 122 0x7a
ESC 27 0x1b ; 59 0x3b [ 91 0x5b { 123 0x7b
FS 28 0x1c < 60 0x3c \ 92 0x5c | 124 0x7c
GS 29 0x1d = 61 0x3d ] 93 0x5d } 125 0x7d
RS 30 0x1e > 62 0x3e ^ 94 0x5e ~ 126 0x7e
US 31 0x1f ? 63 0x3f _ 95 0x5f DEL 127 0x7f



プログラミングⅠ資料No.5
今日の課題
色々な型の変数(char,int,float,double),if文

例題5-1
標準入力からアルファベットの小文字を1文字読み込み,何番目の文字かを示すプログラム
#include <stdio.h>
int main(void)
{
char komoji;
printf("アルファベットの小文字を入力してください ->");
komoji = getchar();
printf("この文字は %d番目です.\n", komoji - 96);
printf("この文字は %d番目です.\n", komoji - 'a' + 1);
}
文字型の変数は,整数型の変数と同じように加減などの演算ができます.

例題5-2
文字型変数の扱い方と表示の例題
#include <stdio.h>
int main(void)
{
char a1,a2;
int b1,b2;
a1=65;
b1='A';
printf("文字型変数:65 ->文字%c\t65 ->数字%d\n",a1,a1);
printf("整数型変数:'A' ->文字%c\t'A' ->数字%d\n",b1,b1);
a1+=5;
b1+=5;
printf("文字型変数:65+5 ->文字%c\t65+5 ->数字%d\n",a1,a1);
printf("整数型変数:'A'+5 ->文字%c\t'A'+5->数字%d\n",b1,b1);
a2=b2=127;
printf("a2=%d\tb2=%d\n",a2,b2);
a2++;
b2++;
printf("a2=%d\tb2=%d\n",a2,b2);
}
文字型変数も整数型変数も同じように使えますが,違いは扱える数の範囲です.文字型変数(8ビット)の下限は-128,上限は127になっています.

練習問題
色々な変数のバイト数を表示します.8ビットで1バイトになります.
#include <stdio.h>
int main(void)
{
char x1;
int x2;
float x3;
double x4;
printf("%d %d %d %d\n",sizeof(char),sizeof(int),
sizeof(float),sizeof(double));
printf("%d %d %d %d\n",sizeof(x1),sizeof(x2),
sizeof(x3),sizeof(x4));
}
長い文では,一つの文を複数行に分割しても構いません.
コンピュータにおける数値の扱い
整数型や実数型の変数は,それぞれすべての整数や実数を扱えるわけではなく,使用できる数値の範囲に制限があります.この制限は,使用しているコンピュータやコンパイラによって異なるため,あらゆるシステムに対して明確に範囲を示すことはできません.
整数として扱う型
char -128~127 (127=2^7-1)
int -32768~32767 (32767=2^15-1)
charは1バイトの大きさであり,‐128~127の範囲の数を表します.文字型と呼ばれると特別な変数のように感じますが,-128~127の範囲の整数を扱う整数型の変数と考えた方が良いでしょう.

実数(浮動小数点)として扱う型
float 単精度の実数(7桁程度)
double 倍精度の実数(14桁程度)
floatとして宣言する実数型は,浮動小数点を10進7桁分の精度で扱えるようになっています.double型はfloat型以上の精度を持つ実数型と定められており,浮動小数点を10進15桁程度の精度で表現できるものが多くなっています.この数の制限は,主にコンピュータの最も基本的な演算命令が何ビット(2進数の桁数)の数を扱うように設計されているかによって異なってきます.
数値の範囲は処理系によって異なるので,新しい型を初めて使うときには,実際に数値を代入してテストした方が安全です.

条件判断
プログラミングでは,if文を使った条件判断は,繰り返しとともに重要なものです.
if文では括弧の中の条件が満たされる場合,後ろの文を実行します.{}を使えば,複数の文を実行することが可能です.
if(a==10)b=3; /*aが10ならbに3を代入する*/

if(a*10>30) {b=3;
c++;
d=0;}
/* a*10が30より大きいとき,bに3を代入し,cを1増やし,dに0を代入する*/

関係演算
a==b aとbが等しい a>b aはbより大きい
a!=b aとbが等しくない a<=b aはb以下である
a>=b aはb以上である a<b aはbより小さい

例題
if文を使った例題
#include <stdio.h>
int main(void)
{
char moji;
printf("一文字入力してください");
moji=getchar();
if(moji=='A')
printf("入力した文字はAです\n");
}

例題
読み込んだ数値で条件の判断を行う
#include <stdio.h>
int main(void)
{
int num;
scanf("%d",&num);
printf("入力した値=%d\n",num);
if(num)printf("条件が満たされる\n");
}
-1,0,1,5などを打ち込んで実行してみましょう.実際にはif文の括弧の中の数値が0以外のときに,ifに続く文が実行されます.

練習問題
標準入力から整数を入力し,数値が自然数(1以上の整数)のときには「入力した値は自然数です」と表示するプログラムを単純なif文(elseなどを使わない)で作ってみましょう.

練習問題
標準入力から文字(アルファベット)を入力し,文字が'A'でない場合,入力した文字を表示するプログラムを単純なif文(elseなどを使わない)で作ってみましょう.

本日のミニプログラム
#include <stdio.h>
int main(void)
{
if(3>2)printf("OK");
if(3<2)printf("NG");
}

プログラミングⅠ資料No.6
今日の課題
複合条件,else,else if,ブロック

例題6-1
複合条件を使って入力した文字が大文字の場合小文字に変換するプログラム
#include <stdio.h>
int main(void)
{
char moji;
printf("一文字入力してください>>");
moji=getchar();
if(moji>='A' && moji<='Z'){
printf("%c -> %c\n",moji,moji-'A'+'a');
}else{
putchar(moji);
putchar('\n');
}
}
この例題でputchar(moji);putchar('\n');とprintf("%c\n",moji);は同じ処理になります.

複合的な条件
演算 演算子 条件式 意味 例
AND && a && b aかつbである i>0 && i<=10
OR || a || b aまたはbである i==-10 || i==10
NOT ! !a aでない !(i>10)

elseを使ったif文
if(条件式)文1;
else文2;
条件が成立した場合(真)文1を実行し,成立しない場合(偽)文2を実行する.
if文で複数の命令を実行させたい場合,ブロックを使います.
if(条件式)
{文1; 文2; 文3;...}
else
{文A; 文B; 文C;...}
条件が成立した場合(真)文1,文2...を実行し,成立しない場合(偽)文A,文B...を実行する.

ブロック
複数の文を{}に入れると,1つの文と同じように扱えます.これをブロックと呼びます.ブロックでは,見やすいところに改行を入れてください.

練習問題6-1
標準入力から整数を読み込み,その整数が20以下の自然数である場合に,その値を表示するプログラムを作りましょう.

例題6-2
整数を読み込み,奇数か偶数かを判定する.
#include <stdio.h>
int main(void)
{
int num;
printf("整数を入力してください ->");
scanf("%d",&num);
if(num%2 == 0){printf("この整数は偶数です.\n");}
else{printf("この整数は奇数です.\n");}
}
この例題でif()とelseには1文しかありませんので,{}は省略して構いません.

例題6-3
文字を読み込み,大文字の場合小文字に変換し,小文字の場合大文字に変換する.それ以外はそのまま表示する.
#include <stdio.h>
int main(void)
{
char moji;
printf("一文字入力してください>>");
moji=getchar();
if(moji>='A' && moji<='Z'){
printf("%c -> %c\n",moji,moji-'A'+'a');
}else if(moji>='a' && moji<='z'){
printf("%c -> %c\n",moji,moji-'a'+'A');}
else{
printf("%c\n",moji);
}
}

練習問題6-2
入力された自然数を4で割った際の剰余で分類し,その分類を表示するプログラムを,else ifを使って作りましょう.

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int i=-10;
if(i<0)printf("マイナス");
else if(i==0)printf("ゼロ");
else printf("プラス");
}


プログラミングⅠ資料No.7
今日の課題
複雑な条件分岐,#define,数学関数,場合分け(swichとcase)

例題7-1
#include <stdio.h>
#define AP 90
#define A 80
#define B 70
#define C 60
#define D 50
int main(void)
{
int a,b,c,gokei;
printf("中間試験の得点");
scanf("%d",&a);
printf("期末試験の得点");
scanf("%d",&b);
printf("出席回数");
scanf("%d",&c);
gokei=(int)(a*0.4+b*0.6);
if(c<10)printf("不合格");
else if(gokei>=AP)printf("A+\n");
else if(gokei>=A)printf("A\n");
else if(gokei>=B)printf("B\n");
else if(gokei>=C)printf("C\n");
else if(gokei>=D)printf("D\n");
else printf("不合格");
}

#define文
#define文ではAPを90に,Aを80に...置き換えます.つまりコンパイルする前に,置換機能を用いて,文字を置換したのと同じことになります.見かけ上変数APを定義して,90を代入するのと同じような働きをしますが,#define文では変数を使いません.
#define文では見かけ上変数のように扱うことができますが,メモリを必要としません.またプログラム中で頻繁に使う値を変更したい場合などに便利です.

型変換
(型)値とすると,値を違う型に変換できます.
整数から実数,または実数から整数への型変換はよく使われます.
(例) i=(int)(100.0*sqrt(2.0)); y=(double)i/250;  x=(float)1;

例題7-2数学関数を使う
#include <stdio.h>
#include <math.h>

int main(void)
{
float a=4.0,b=2.0;
printf("%f\t%f\n",sqrt(a),sqrt(b));
printf("%f\n",exp(1.0));
printf("%f\n",atan(1.0)*4.0);
}

数学関数を使うプログラムとそのコンパイルの方法
sqrt(a)のような平方根を求めるものは,数学関数として用意されています.これらの数学関数を利用するためには,int  main(void)の行の前に,予めこれらを利用することを記述しておく必要があります.例題のように,#include <math.h>
という行を書いてください.
コンパイラによっては,オプション -lm (マイナス,小文字のエル,エム)をつける必要があります.数学関数の引数は一般にdouble型(float型よりも扱う桁数が大きい)ですが,float型を使っても構いません.特に高い精度で計算を行いたい場合,float型よりもdouble型の変数を使って下さい.

例題7-3直角三角形の斜辺の長さを求める
#include <stdio.h>
#include <math.h>

int main(void)
{
float a,b,c;
printf("辺1\n");
scanf("%f",&a);
printf("辺1\n");
scanf("%f",&b);
c=sqrt(a*a+b*b);
printf("斜辺の長さ=%f\n",c);
}

倍精度実数の読み込み
double型の変数をscanf()で読み込むときは,scanf("%lf",&a);のようにパーセントエルエフとしてください.

例題7-4
#include <stdio.h>
#include <math.h>
#define PI 3.14159265
int main(void)
{
float x;
x=0.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=15.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=30.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=45.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=60.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=75.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
x=90.0;
printf("%f,%f\n",x,cos(x/180.0*PI));
}

角度に関する関数の単位は度(degree)ではなくradian(180度=πrad)ですので気をつけてください.

switchとcase
switchを使うと括弧の中の値に対応するcaseに飛んで実行を行います.対応するcaseが無い場合,default:に飛びます.caseは行き先表示のようなもので文ではないので,最後はコロン:です.またcaseに飛んだあと,switchのブロックから抜け出す命令がbreakです.

例題7-5switchとcaseを使った場合分け
#include <stdio.h>
#define Y1 "子\n"
#define Y2 "丑\n"
#define Y3 "寅\n"
#define Y4 "卯\n"

int main(void)
{
int i,j;
printf("生年を西暦で入力してください\n");
scanf("%d",&i);
j=i%12;
switch(j){
case 4:
printf(Y1);
break;
case 5:
printf(Y2);
break;
case 6:
printf(Y3);
break;
case 7:
printf(Y4);
break;
default:
printf("子から卯以外ですね\n");
break;
}
}

本日のミニプログラム
#include <stdio.h>
#include <math.h>
#define PI 3.1415
int main(void)
{
printf("%f",sin(30.0/180*PI));
}


プログラミングⅠ資料No.8
今日の課題
whileを使った繰り返し,乱数,型変換

例題8-1whileを使った繰り返し
1から入力された数までの総合計を計算する
#include <stdio.h>
int main(void)
{
int i=1,n,total=0;
printf("1~nまで加えます,nを入力してください");
scanf("%d",&n);
while(i<=n){
total+=i;
printf("現時点の値=%d\t合計%d\n",i,total);
i++;
}
printf("最終的な合計=%d\n",total);
}
whileを使った繰り返し
while(条件){ブロック}
while文では括弧の中()の条件が正しいかチェックし,正しければ以下のブロック{}を実行します.条件が正しければ,何度でも繰り返してブロック{}を実行します.以下のように書くと,iが1からnまで処理を繰り返します.繰り返しの処理を終わったあと,iの値はn+1になっています.
i=1;
while(i<=n){
処理
i++;
}

練習問題1上の例題のプログラムでi++; の行を消してコンパイルし,実行してみましょう.何が起こるでしょうか.
ヒント:放置すればたぶん永遠に動き続けることでしょう.このように誤って終れなくなる場合,無限ループあるいはエンドレスループ(endless loop)におちいったと言います.Ctrl-Cを入力して中断しましょう.
練習問題2上の例題ををデクリメントi--;を使って書き替えなさい.
練習問題31~10までの各整数の2乗,3乗,平方根を求めて表示するプログラムを書いてみましょう.

例題8-2入力された数の階乗を計算する.階乗n!=n×(n-1)×(n-2)×…×1で,0!=1です.
マイナスの数が入力されたらエラーとします.

#include <stdio.h>
int main(void)
{
int n,i=1;
float a=1.0;
printf("nの階乗n!を計算します");
scanf("%d",&n);
if(n<0){printf("ERROR:nは0以上の整数\n");}
else{
while(i<=n){
a*=i;
i++;
}
printf("%d!=%f\n",n,a);
}
}
またprintf("%d!=%f\n",num,result);の%fを%eに書き替えて実行してみましょう.
%eは6.204484e+23=6.204484×1023のように,実数を指数表示します.

練習問題4実行結果をMathematicaで確認しましょう.
20!(Shift+Enter)
N[%](Shift+Enter)

例題8-3
rand()は整数型の乱数を作成します.#include <stdlib.h>を記述すると利用できます.乱数生成のされ方や周期はシステムによって異なります.乱数の最大値はRAND_MAXという定数に定義されています.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i;
float x=0.0;
srand(100);
while(x<=2.0/3.0){
x=(float)rand()/RAND_MAX;
if(x<1.0/3.0)printf("グー\n");
else if(x<2.0/3.0)printf("チョキ\n");
else printf("パー\n");
}
}
乱数から0-1の範囲の実数型の乱数を作り,0-1/3:グー,1/3-2/3:チョキ,2/3-1:パーを表示し,パーが出たら終了する.
srand()は乱数のseed(種)を決定するもので,srand()の引数カッコの中の数を変えることで乱数の条件を変えることができます.

型変換
(型)値とすると,値を違う型に変換できます.x=(float)rand()/RAND_MAX;の場合,乱数の値rand()は,整数ですが,実数に変換され,そのあとにRAND_MAXで割り算されます.x=1.0*rand()/RAND_MAX;としても同じ結果が得られます.(実数)/(整数)の結果は,実数となります.
型変換は,整数から実数,または実数から整数への変換などに使われます.
(例) i=(int)(100.0*sqrt(2.0)); y=(double)i/250;  x=(float)1;

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int i=1,a=0;
while(i<=10){
a+=i;
i++;
}
printf("%d",a);
}


プログラミングⅠ資料No.9
本日の課題
EOF,breakとcontinue,do while文

知っておくと便利なループの流れについて
break文
break文はswitch case文や,while文またはfor文などで作られる繰り返し処理(ループ)から抜け出すときに用いられます.多重の繰り返し処理の場合,break文で最も内側の繰り返し処理から脱出します.
continue文
continue文は,繰り返し処理の残りの部分を省略して繰り返し処理の先頭に戻ります.
無限ループ
繰り返し処理で条件が常に満たされる場合,無限ループとなりプログラムが終了しません.無限ループを止めたい場合,強制的にプログラムを終了する必要があります.

例題9-1入力した文字の終了EOFが来るまで文字の表示を続ける.
#include <stdio.h>
int main(void)
{
char c;
while((c=getchar())!=EOF){
printf("%cのコード番号%d\n",c,c);
}
}
このプログラムでは,半角文字が入力されるとその文字をそのまま表示する.
入力を終了したいときは,Ctrl+Z Enter(Windows),Ctrl+C(Unix)を入力する.
半角文字(ASCIIコード)は,0-127の数値に相当するので,文字が入力されれば0-127の数値がcに代入されます.
文章の最後を意味する信号が入力されると,cに-1が代入されます.C言語では以下のようにEOFが-1と定義されています.
#define EOF (-1)
従ってwhile((c=getchar())!=EOF)とすれば,文章の最後を意味する信号が入力されるとwhile文が終了します.
例題9-2入力した文字が数字の場合break文で読み込みを終了する

#include <stdio.h>
int main(void)
{
char c;
while((c=getchar())!=EOF){
if(c>='0' && c<='9')break;
putchar(c);
}
}

練習問題文字'x'が入力されたとき終了するプログラムを作ってください.

do while文while文は,ループの先頭で処理を繰り返すかどうかの判断を行いますが,これに対し,do while文はループの終末で繰り返すかどうかの判断を行います.do whileは文を実行した後で条件式を評価し,条件式が真である間,文を繰り返します.
繰り返し終了前に少なくとも1度は,ループ内の処理を実行させたい場合などに便利です.

例題9-3
1から100までの合計を表示する

#include <stdio.h>
int main(void)
{
int i=1,s=0;
do{
s+=i;
i++;
}while(i<=100);
printf("%d",s);
}

練習問題
例題をデクリメントi--を使って書き替えなさい.

練習問題do whileを使って,初期値と公差を入力しiが0から9番目までの等差数列の和を求めるプログラムを書きましょう.

例題9-4整数の入力を繰り返し,0が入力されたら合計を表示し終了する
#include <stdio.h>
int main(void)
{
int i,s=0;
do{
scanf("%d",&i);
s+=i;
}while(i!=0);
printf("合計は%d\n",s);
}

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int a=100,b=0;
do{
b+=a;
a--;
}while(a>0);
printf("%d",b);
}



プログラミングⅠ資料No.10
今日の課題
for文(forループ)

for文について
繰り返し回数があらかじめ決まっているループを実行させる場合には,for文が用いられます.for文の一般的な構造は次のようになります.
for(初期値設定; 継続条件 ; 更新式){文;文;...}
処理を行う文が一つしかない場合にはカッコ{}を省略して
次のように書けます.
for(初期値設定; 継続条件 ; 更新式)文;
for文の( )中の「初期値設定」と「更新式」を省略すると,while文と同じことになり,';'以外のすべてを省略すると無限ループになります.
for (i= 5; i < 10; i++)
iを5から始め,iを+1しながら10未満まで(9まで)の間繰り返す.
for (i= 10; i > 0; i--)
iを10から始め,iを-1しながらiが0より大きい間繰り返す.
for (c= 'a'; c <='z'; c++)
cを'a'から'z'まで繰り返す.
for ( x = 0.0; x <=360.0; x = x + 20.0)
xを0.0から始め,+20.0しながら360.0になるまで繰り返す.
for ( ; ; )
判断式がないので,無限ループとなる.

例題10-1
1から入力された数までの合計
#include <stdio.h>
int main(void)
{
int i,sum=0,n;
printf("整数nを入力してください");
scanf("%d",&n);
for(i=1;i<=n;i++){
sum+=i;
}
printf("1からnまでの合計=%d\n",sum);
}

練習問題2から入力された数までの偶数の合計を求めるプログラムを作りなさい.

例題10-2
aから入力した文字までの表示を行う.
#include <stdio.h>
int main(void)
{
char a,b;
printf("英小文字を入力してください\n");
scanf("%c",&a);
printf("aから入力した文字までの表示\n");
for(b='a';b<=a;b++)printf("%c\n",b);
}

練習問題
1~10までの各整数に関して各数の2乗,3乗,平方根を求めて表示するプログラムをfor文を用いて書いてみましょう.

例題10-31から100までの3の倍数の個数を求める
#include <stdio.h>
int main(void)
{
int i,s=0;
for(i=1;i<=100;i++){
if(i%3 == 0){printf("%d\n",i); s++;}
}
printf("1から100までの3の倍数の個数は%d\n",s);
}

練習問題
上のプログラムをwhile文を用いて書いてみましょう.
練習問題
上のプログラムをデクリメント(i--)を用いて書いてみましょう.

例題10-4入力された整数が素数かどうか判定する.
#include <stdio.h>
int main(void)
{
int i,n;
printf("整数nを入力してください\n");
scanf("%d",&n);
for(i=2;i<n;i++){
if(n%i==0){
printf("素数ではありません\n");
break;
}
}
if(i==n)printf("素数です\n");
}

(注)break文を実行しないで,繰り返しを最後まで終了した場合,,i==nになることに注意しましょう.


例題10-52重のforループ
鶴と亀が合計で10匹,足の数が合計で26本の場合の鶴亀算.
#include <stdio.h>
#define N 10
#define M 26
int main(void)
{
int i,j,hiki,ashi,turu,kame;
for(i=0;i<=N;i++){
for(j=0;j<=N;j++){
hiki=i+j;
ashi=i*2+j*4;
if(hiki==N && ashi==M){turu=i;
kame=j;
}
}
}
printf("正解 鶴は%d匹 亀は%d匹\n",turu,kame);
}

多重のループの場合カッコ{}の組み合わせが合っているか確認しましょう.
練習問題タコとイカが合計で100匹,足の数が合計で840本の場合のタコとイカの数を求めなさい.

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int i;
float s=1.0;
for(i=1;i<=10;i++)s*=i;
printf("%f",s);
}


プログラミングⅠ資料No.11
今日の課題
簡単な関数,main関数とユーザー関数,プロトタイプ宣言

例題11-1 2つの数の差を関数を使って計算する.
#include <stdio.h>

int sa(int x,int y){
int z;
if(x>=y)z=x-y;
else z=y-x;
return z;
}

int main(void)
{
int i,j;
printf("数を入力してください\n");
scanf("%d",&i);
printf("数を入力してください\n");
scanf("%d",&j);
printf("%dと%dの差は%d\n",i,j,sa(i,j));
}

関数
C言語のプログラムを実行したときに,必ず実行されるのがmain(){}の記述です.これをmain(メイン)関数と呼びます.
複雑な処理をプログラムで記述する場合や同じ処理を繰り返す場合,まとまった処理を好きなときに呼び出せると便利です.ユーザーが作った処理の手順をユーザー関数やユーザー定義関数と呼びます.
main関数からユーザー関数を利用する場合,ユーザー関数へいくつかの値が送られます.またユーザー関数からmain関数へ処理の結果を値として送ります.main関数からユーザー関数へ送る値を引数(ひきすう)と呼びます.ユーザー関数からmain関数へ送る値を戻り値と呼びます.
戻り値=関数の型
関数定義の中のreturn(式) の式の値が関数の戻り値となります.戻り値がどのような型なのかを関数の型として明確に宣言しておかなければなりません.return(式)の型と関数の型は一致していなければなりません.戻り値は1個または無し(void)です.
引数
main関数側で指定する引数を 実引数(じつひきすう),ユーザー関数側で指定する引数を仮引数(かりひきすう)と呼びます.関数を呼び出すと,仮引数に実引数の値が代入されると考えればよいでしょう.従って,実引数には,定数,変数,式(定数や変数を演算子で結合したもの) を書けますが,仮引数には,変数しか書けません.また,仮引数は関数を定義する際,型を宣言しておく必要があります.対応する実引数と仮引数には同じ名前を付ける必要はありませんが,型と個数は必ず一致させなければなりません.引数の無い関数(void)もあります.

異なる関数の中で定義される変数は,同じ名前でも,別なものですので注意しましょう.

練習問題
和を計算する関数wa(x,y)を使ったプログラムに書き換えましょう.

例題11-2 例題10-1を改良し,関数を使い1からiまでの合計を表示するプログラムです.
関数を使う場合,引数をどうするか?戻り値をどうするか?変数の定義が,引数と関数内で重複しないか,注意する必要があります.戻り値の型と関数の型が一致していることも確認しましょう.

#include <stdio.h>
int gokei(int n){
int i,sum=0;
for(i=1;i<=n;i++){
sum+=i;
}
return sum;
}
int main(void)
{
int i;
for(i=1;i<=20;i++)
printf("1から%dまでの合計=%d\n",i,gokei(i));
}

(参考)例題10-1
#include <stdio.h>
int main(void)
{
int i,sum=0,n;
printf("整数nを入力してください");
scanf("%d",&n);
for(i=1;i<=n;i++){
sum+=i;
}
printf("1からnまでの合計=%d\n",sum);
}

例題11-3 整数の階乗を返す関数
#include <stdio.h>

float kai(int n)
{
float x=1.0;
int i;
for(i=1;i<=n;i++)x*=i;
return x;
}

int main(void)
{
int i;
for(i=1;i<=10;i++){
printf("%dの階乗は%f\n",i,kai(i));
}
}
main()関数の中で変数iと関数kai()の中で使われている変数は,同じ名前でも,別なものですので注意しましょう.

例題11-4 プロトタイプ宣言を使った階乗を返すプログラム
#include <stdio.h>

float kai(int n);

int main(void)
{
int i;
for(i=1;i<=10;i++){
printf("%dの階乗は%f\n",i,kai(i));
}
}

float kai(int n)
{
float x=1.0;
int i;
for(i=1;i<=n;i++)x*=i;
return x;
}

関数のプロトタイプ宣言
普通はmain関数の前に,使用する関数を記述しておきます.そうしないと計算機は,main関数の中にあるユーザー定義関数の引数の型と戻り値の型が,どのようなものなのか分からないので,コンパイルできません.
しかしプログラムの流れとして,main関数を最初に書いた方が分かりやすい場合があります.このときに,関数の名前,引数の型,戻り値の型(関数の型)をmain関数の前に宣言しておき,詳細はmain関数の後に記述することができます.関数の名前と型をmain関数の前に宣言することを,プロトタイプ宣言と言います.
実際には,ユーザー定義関数の先頭の行の")"までをmain関数の前に置いて,最後にセミコロン";"を付ければ,プロトタイプ宣言になります.またプロトタイプ宣言では,関数の名前,引数の型,戻り値の型を定義すれば構いません.
int p(int x,int y); または int p(int,int); どちらでも構いません.

練習問題
上の例題でプロトタイプ宣言の行を削除してコンパイルしてみましょう.

練習問題
例題11-1をプロトタイプ宣言を使ったプログラムに書き換えましょう.

一歩進んで
printf()やgetchar()などの関数も,本当はプロトタイプ宣言が必要です.どこにあるのでしょうか?実はプログラムを書くときのおまじないとして扱っていた,#include<stdio.h>という部分です.実際は,stdio.hというファイルが用意されており,その中にprintf()やgetchar()などの関数のプロトタイプ宣言が書かれています.ところでmain関数も関数の一つですが,main関数はint型で定義されています.C言語の本によってはmain関数の最後にreturn 0;を入れて形式的に整数を返している書き方があります.

練習問題
xの絶対値を返す関数をプロトタイプ宣言を使って作ってみましょう.

練習問題
xのn乗を返す関数をプロトタイプ宣言を使って作ってみましょう.

本日のミニプログラム
#include <stdio.h>
int wa(int x,int y)
{
return x+y;
}
int main(void)
{
printf("%d",wa(2,3));
}


プログラミングⅠ資料No.12
本日の課題
関数その2(戻り値の無い関数,引数の無い関数,関数の内部の変数,グローバル変数)

戻り値のない関数
この場合,関数や引数の型の定義をvoidとします.戻り値の無い関数では,関数の型がvoidとなり,return文はなくなります.
例題12-1 戻り値の無い関数の例.値が4で割り切れるときに値を表示する関数を使い,100までの7の倍数の中から4の倍数を表示する.
#include <stdio.h>

void test(int x)
{
if(x%4==0)printf("%d\n",x);
}

int main(void)
{
int i;
for(i=7;i<=100;i+=7)test(i);
}

練習問題
プログラムをプロトタイプ宣言を使って書き直しましょう.
引数の無い関数
引数の無い関数では,引数の部分がvoidとなります.
例題12-2
関数の内部で整数の値を読み込み0以上なら平方根を返し,負なら0を返す関数
#include <stdio.h>
#include <math.h>

float test(void)
{
int i;
float x;
scanf("%d",&i);
x=(float)i;
if(i>=0)return sqrt(x);
else return 0.0;
}
int main(void)
{
printf("%f\n",test());
}

例題12-3関数における値の確認.main関数とユーザー関数内の変数の変化に注意しましょう.
#include <stdio.h>

int test(int x,int y)
{
int z=0;
x*=2;
y--;
printf("x=%d y=%d z=%d \n",x,y,z);
return x;
}

int main(void)
{
int x=3,y=4,z=0;
printf("x=%d y=%d z=%d \n",x,y,z);
z=test(x,y);
printf("x=%d y=%d z=%d \n",x,y,z);
}

局所的変数(ローカル変数)
今まで使ってきた変数は,一般にその関数内だけで有効であり,これを局所的変数(ローカル変数)と呼びます.main関数やユーザー関数内で同じ変数名が使用されていても,これらは別な物として扱われ,お互いに影響を与えることはありません.
グローバル変数(外部変数)
これに対し,main関数やユーザー関数の外側で定義した変数をグローバル変数(外部変数)と呼び,どの関数からも共通して呼び出せます.ユーザー関数からreturnで値を一つしか戻せませんが,ユーザー関数で得られた複数の値をmain関数でも使いたいときなど便利です.
しかし,プログラムが複雑になると,どこかで変数を書き替えたのに気がつかず,トラブルが生じることがあるので,注意が必要です.

例題12-4
グローバル変数を使う例題
#include <stdio.h>
#include <math.h>
int i;
void func(void)
{
float x;
x=cos(i*3.14159/180.0);
printf("%d %f\n",i,x);
}
int main(void)
{
for(i=0;i<=90;i++)func();
}

#include <math.h>ではM_PIとして円周率が与えられています.

例題12-5
グローバル変数a,b,cを用いて2次方程式の解を求める.
#include<stdio.h>
#include<math.h>
float a,b,c; /*グローバル変数*/
void kai(void)
{
float a1,a2,d;
d=b*b-4.0*a*c; /*判別式*/
a1=(-b+sqrt(d))/(2.0*a);
a2=(-b-sqrt(d))/(2.0*a);
printf("解1=%f\n解2=%f\n",a1,a2);
}

int main(void)
{
printf("係数aを入力してください");
scanf("%f",&a);
printf("係数bを入力してください");
scanf("%f",&b);
printf("係数cを入力してください");
scanf("%f",&c);
if(b*b-4.0*a*c>=0)kai();
else printf("解はありません\n");
}

変数をdoubleにしてscanf()を使う場合scanf("%lf",&a);のように%lfとしてください.
本日のミニプログラム
#include <stdio.h>
int i;
void test(void)
{
printf("%d",i);
}
int main(void)
{
i=5;
test();
}

プログラミングⅠ資料No.13
今日の課題
配列の使い方その1

配列について
同じ型の変数を多数使いたい場合に,一つ一つ異なる変数名を割り当てるのは大変です.プログラムが見にくく,エラーの原因にもなります.このような場合は,配列を用います.
例えば, int a[40]; と宣言すると,a[0],a[1],…,a[39]という40個の変数が用意されます.変数はデータを入れておく箱として考えることができます.配列の場合,図のように aという配列名と[ ]で囲まれた配列番号 (添字と呼ぶ) で管理されます.つまり,a[0]は0番目の箱,a[39]は39番目の箱という意味になります.i番目の箱はa[i]と書くことで指定できます.配列の添え字は必ずゼロからスタートします.

例題13-1
#include <stdio.h>
#include <math.h>
int main(void)
{
int a[11],i;
float b[11];
for(i=0;i<=10;i++){
a[i]=10-i;
b[i]=sqrt(10.0-i);}
for(i=0;i<=10;i++)
printf("a[%d]=%d\tb[%d]=%f\n",i,a[i],i,b[i]);

}

配列の定義
C言語における配列の宣言は,以下のようにして行います.
int suu[20]; /* 整数型の配列suu[0]- suu[19]*/
float data[5][15]; /* 単精度実数型の二次元配列data[0][0]- data[4][14]*/
char moji[20]; /* 文字型の配列moji[0]- moji[19]*/
型としては,int,float,double,charなど,これまで扱った変数と同じ型を指定できます.要素のカッコ[]が1つのものを1次元配列,2つのものを2次元配列と呼びます.
ここで特に注意しておきたいのは,指定する[要素の数]は添字の上限ではなく,要素の数であるということです.つまり,int a[40]; と宣言された配列は,a[0]~a[39]までの40個が用意されるのであって,a[40]という配列要素は確保されていません.

配列の初期化
int suu[20]; として配列を定義してから suu[18]++; とするとどうなるでしょうか?suu[18]が初めから0であるとは限りません.初めから配列に値を入れておくか(初期化),正しく配列に値を代入する必要があります.例えば配列に全て0を代入する場合でも,forループなどが必要です.しかし要素の数が小さい配列の場合,配列の宣言時に,{ }の中にデータを書いておけば,自動的にデータを初期設定することができます.

また,配列の宣言時に初期化データが置かれているときは,[]内の値を省略して
int a[]={3,2,1};
のように書くこともできます.


例題13-2
配列を使い平均,分散,標準偏差を計算する
平均   分散   標準偏差

#include <stdio.h>
#include <math.h>
#define N 5
int main(void)
{
float a[N]={1.0,2.0,3.0,4.0,5.0};
int i;
float s=0.0,heikin,bunsan;
for(i=0;i<N;i++)s+=a[i];
heikin=s/N;
printf("平均は%f\n",heikin);
s=0.0;
for(i=0;i<N;i++)s+=(a[i]-heikin)*(a[i]-heikin);
bunsan=s/N;
printf("分散は%f\n標準偏差は%f\n",bunsan,sqrt(bunsan));
}
例題13-3
配列の最大値を表示するプログラム
#include <stdio.h>
int main(void)
{
int data[8]={2,4,3,10,0,15,-3,1};
int i,max;
max=data[0];
for(i=1;i<8;i++){
if(data[i]>max)max=data[i];
}
printf("最大値は%d\n",max);
}

変数maxにdata[0]の値を入れておき,配列の要素が,maxを上回ったときに値を更新します.
同じ型の変数と配列の定義は,以下のように一緒にしても,分けてもどちらでも構いません.
int i,max,data[8]={2,4,3,10,0,15,-3,1};

例題13-4
素数(prime number)を計算する.
最初は配列s[]に全て1を入れておきます.
素数の倍数の場合s[i]=0としていくと,s[i]==1のiが素数となります.

#include <stdio.h>
#define N 1000
int main(void)
{
int i,s[N],sp;
for(i=2;i<N;i++)s[i]=1; /* 配列s[]を全て1で初期化*/
sp=2; /*最初の素数は2*/
while(sp<N){
for(i=2*sp;i<N;i+=sp)s[i]=0; /*素数spの2,3,4..倍を消していく*/
for(i=sp+1;s[i]!=1;i++); /*素数spの次の素数を探しspとする*/
sp=i;
}
for(i=2;i<N;i++)if(s[i]==1)printf("%d\n",i);
}


一歩進んだ例題
2次元配列を使った掃き出し法による逆行列の計算
#include <stdio.h>
int main(void)
{
int i,j,k,n=3;
float a,b,y[3][3];
float x[3][3]={{1.0,2.0,-1.0},{-1.0,-1.0,2.0},{2.0,-1.0,1.0}};
/* y[3][3]は単位行列*/
for(i=0;i<n;i++)for(j=0;j<n;j++)if(i==j){y[i][j]=1.0;}
else{y[i][j]=0.0;}
for(i=0;i<n;i++){a=x[i][i]; /* X の対角成分を1にする*/
for(j=i;j<n;j++)x[i][j]=x[i][j]/a;
for(j=0;j<n;j++)y[i][j]=y[i][j]/a;

for(j=0;j<n;j++)if(j!=i){b=x[j][i];
for(k=i;k<n;k++)x[j][k]=x[j][k]-x[i][k]*b;
for(k=0;k<n;k++)y[j][k]=y[j][k]-y[i][k]*b;
}
}
printf("%f %f %f\n",y[0][0],y[0][1],y[0][2]);
printf("%f %f %f\n",y[1][0],y[1][1],y[1][2]);
printf("%f %f %f\n",y[2][0],y[2][1],y[2][2]);
}

掃き出し法:
逆行列を求めたい行列Aと単位行列を横に並べ3×6行の行列[AE]を作る.
行基本操作を行い,行列Aを単位行列にすると,Eに相当する部分がAの逆行列になる.
行基本操作
1つの行にある数をかける.
1つの行にある数をかけたものを別の行に加える.
2つの行を入れ替える

本日のミニプログラム
#include <stdio.h>
int main(void)
{
int a[4]={1,2,3,4};
int i;
for(i=0;i<4;i++)printf("%d\n",a[i]);
}

プログラミングⅠ資料No.14
今日の課題
配列の続き,文字列と配列,プログラミングⅠのまとめ

例題14-1
文字型配列を使った,文字列の入出力.
#include <stdio.h>
#define N 20
int main(void)
{
char a[N];
int i;
printf("文字列を入力して下さい");
scanf("%s",a);
printf("%s\n",a);
for(i=0;i<N;i++)putchar(a[i]);
}

文字と文字列の違い
文字は文字型変数に代入することができ,整数と同じように加減算などができます.
文字は'z'や'\n'のようにシングルコーテーション「'」で囲みます.
文字列は"abc"のようにダブルコーテーション「"」で囲みます.実際には,文字列"abc"の場合,'a'+'b'+'c'+'\0'のように最後に,文字列の終端を意味するヌル文字'\0'が付きます.
一文字の文字列"a"と文字定数'a'は異なることに注意しましょう.
配列に文字列を代入する
単語のように文字数が決まっていない文字列を配列に格納する場合,文字列の終端を意味するヌル文字'\0'を使います.十分な大きさの配列を用意しておけば,文字数に関わらず必要な文字列の入出力ができます.ヌル文字より後ろの配列の値は無視されます.
例えば要素の数が5の配列a[]に文字列"abc"を代入すると,各要素は次のようになります.
a[0]='a'
a[1]='b'
a[2]='c'
a[3]='\0'
a[4]=?
5番目の要素a[4]には,何が入っているか未定です.

配列a[]に代入した文字列の表示は,次のように行います.
printf("%s\n",a);
このときヌル文字の前までの文字が表示されます.

例題14-2
scanf()で文字列を入力すると文字が一つずつ配列に代入され最後に'\0'が入ります.'\0'は文字列の終了を意味する文字(ヌル文字)です.'\0'までの数をカウントすることにより文字数を計算できます.
#include <stdio.h>
int main(void)
{
char a[20];
int i=0;
printf("文字列を入力して下さい");
scanf("%s",a);
printf("%s\n",a);
for(i=0;i<20;i++){
if(a[i]=='\0')break;
putchar(a[i]);
}
putchar('\n');
printf("文字数は%dです",i);
}
一歩進んだ説明
scanf()では,ポインタで値の保存先を指定します.&変数名で変数のポインタ(変数のアドレスを示すもの)となります.配列のカッコをはずすとポインタになるので,scanf()で配列に文字列を入力する場合&は不要です.


例題14-3wile,do while,forの比較1から10までの和
(wile)
#include <stdio.h>
int main(void)
{
int i=1,s=0;
do{s+=i;
i++;
}while(i<=10);
printf("%d",s);
}

(do while)
#include <stdio.h>
int main(void)
{
int i=1,s=0;
while(i<=10){
s+=i;
i++;
}
printf("%d",s);
}

(for)
#include <stdio.h>
int main(void)
{
int i,s=0;
for(i=1;i<=10;i++){
s+=i;
}
printf("%d",s);
}

例題
C言語の復習を兼ねた例題
#include <stdio.h>
#include <math.h>
#define PI 3.14159
int main(void)
{
int i=45,j=0;
float x;
printf("sin(%d)=%f\n",i,sin((float)i*PI/180.0));
for(i=1;i<10;i++)if((i%3==0) || (i<5))j+=i;
printf("%d\n",j);
i=4/5+4.0/5;
x=4/5+4.0/5;
printf("%d %f\n",i,x);
printf("アルファベットの小文字を入力してください\n");
scanf("%c",&i);
if(i>='a' && i<='z')printf("%cは%d番目の文字です\n", i,i-'a'+1);
else printf("アルファベットの小文字ではありません\n");
}