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

このブログを検索

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

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);
}