Microchip Studio for AVR
V 7.0
アトメル社が2016年、マイクロチップ社に吸収され、残っていたAtmel Studio の名前もMicrochip Studioに変更されてしまったようです。いくら、AVR
Studio4が安定していたとは言え、次々と誕生する新AVRに対応するためにはMicrochip Studioを無視し続けることはできません。ただ、マイクロチップ社自体、AVRを飲み込んだことは大変らしく、開発環境の細部に至っては変更の嵐が吹いているかのようです。しかしいつまでも待つわけにはいきません。ここにMicrochip
Studioの安定を祈りつつ手探りでスタートすることにします。
●
Microchip Studio for AVR インストール
Microchip
このサイトの Dounload Microchip Studio for AVR and SAM Devices- Web Installer
をクリックします。
インストールの開始で I agree にチェックを入れ、
Select Architecture では AVR だけにチェックを入れます。
Microsoft の Visual Studio も続いてインストールされます。
途中、何回か「このデバイスソフトウェアをインストールしますか?」と尋ねられるので「インストール」を選びます。
インストールが完了したら Close します。
Microchip Studio
●新規プロジェクト
Microchip Studioをスタートするとやがて、下のようなオープニング画面になります。
新しくプログラム開発を開始する場合は Start の New Project から入ります。
画面下の Name にプロジェクト名を入れ
Brouseをクリックしてプロジェクトを作成するフォルダを選択するか Locationで指定します。
一覧の中から GCC Executable Project (実行可能なプロジェクト)を選択してOKします。
次に、使用するAVRのコントローラーの型番を選択します。
Solution Explorer ウィンドーの src(ソースファイル)の main.c をクリックすると
のようなソースファイル画面が現れ、プログラムのコードを書き始めることができます。
●
次回からは
StartのOpen Project → フォルダの中の.atsln ファイルを開きます。
直近に作業したプロジェクトは Recent で表示されるので指定することができます。
●
コンパイル実行
できたソースファイルをコンパイルします。
コンパイルは Build → Build〔プロジェクト〕 をクリックします。
画面下方に Build succeeded. が表示されたらコンパイル完了です。
少しづつコードを書き込んで、コンパイルしてエラーの無いことを確認するのがコツです。
●
旧AVR studio から変換は
file → import で apsファイルを選択します。
Solution Explorer ウィンドーの src(ソースファイル)の main.cでファイルを広げます。
●
必要なヘッダーファイルを組み込む
ソースファイルの冒頭に書く #include文は、マイクロコントローラーの入出力ポートのアドレス定義や各種ビットの定義を設定するファイルを組み込んでくれる働きをします。
少なくとも下のようなヘッダーファイルをincludeしておきます。
#include <asf.h> // Atmel Software Framework ライブラリ
#include <avr/io.h> // AVR入出力用ヘッダファイル
#include <avr/interrupt.h> // AVR割込み用ヘッダファイル
●プログラムをAVRに書き込む
プログラムの動作を確認する方法としてデバッガーを使うという方法もありますが、マイクロコントローラーの中のフラッシュROMに書き込むためには、画面の上部中央の 「Degug」
をクリックして 「Release」 にしておきます。
そして、使用するライターを指定します。
Project → 〔プロジェクト〕 Properties のDevice タグで書き込みツールが表示されます。
上記のような各種ライターを使うことができるようです。
私はAVRisp MKUを持ていたので使うことにしました。ただし、このライターはAtmel社が販売していたものですが、現在は製造中止になっています。AVRisp
MKU〔互換/Compatible〕とされるAVRライターがアマゾンなどから各種販売されているようですから、ユーザーによるMicrochip
Studio7 での書き込み報告などを確認の上、入手することをお勧めします。
●AVRisp MKUで書込み
下は、ATmega164Pと、LEDや、スイッチ、フォトインタラプトなどの回路と、プログラマ-AVRISPmkUに接続した簡単な回路の例です。
ATmagaやATtinyなど AVRコントローラーには
MISO,Vcc,SCK,MOSI,RESET,GND
の各ピンがあるので、AVRispMKUと接続し、PCとUSB接続し、電源5Vを供給します。
回路図1
なお、AVRispMKUが、正常にWindows上で動作可能かどうかはWindowsのシステムで確認することができます。Win10/11 ではAVRispMKUのUSBを接続した状態で、Windowsのスタートボタンを右クリックして デバイスマネージャをクリックすると
上のように Microchip Tools の先に AVRISPmkUが表示されるので、ここを右クリックすることで、状態を確認することができます。ただしこれはWindowsのドライバーが正常か否かを確認するだけなので、ライタとして動作させるためには、次の操作が必要になります。
ライタとして登録するには Microchip Studio のメニューから、
Project → 〔プロジェクト〕 Properties → Tool タグの Selected /Programmer でAVRispMK
Uを指定します。
この設定は最初の一回だけで良いのですが、これをしないと、Microchip Studio は正常に書込みすることはできません。
Programmer を設定できたら Microchip Studioのタブは main.c に戻します。
★
Hint !
入力ポートおよび出力ポートは通常10kΩ程度のプルアップ抵抗を入れます。基板取り扱い時の静電気破壊の防止、入力ポートとして使用する場合の電圧レベルの確定、誘導ノイズの低減などに役立つからです。ただし上例のPA7のように、回路の信号レベルを得るために10k以外の抵抗値が必要とされる場合は注意する必要があります。
もう一点注意、プログラム書き込み中は、5V以外の回路出力が動作して、回路が破損する危険性があります。書込み中は関係しない電源系の回路はOFFしておくようにしてください。
●
プログラミング操作
Tools→Device Programming →Apply を押し
Readを押して 画面下部に Reading device ID..OK が表示されることを確認します。
Fusesタグはデバイスの基本動作を決める重要なヒューズのセッティングです。
特に、HIGH SPIEN はUSBによるシリアル書き込みを可能にするチェックですから、外した状態でデバイスへ書き込むと、以降の書き込みが不可能になってしまうので、絶対外さないでください。
なお、クロックを1/8分周しない高速クロックの場合はCKDIV8のチェックを外します。
SOUT_CKSEL は購入初期においては Int RC Osc (内部発振回路)になっていますが、XTAL1/2端子に外部発振信号を接続しない状態でこれらのモードに設定すると動作が停止してしまうので注意が必要です。書込み装置のXTAL1端子には発振信号を加えておくことを推奨します。
ヒューズのProgram を押してOKとなったら、次は Memories タグ の Frash に作成しているプロジェクトの elf ファイルが表示されているか確認します。表示されていない場合は … を押して、プロジェクトのelf
ファイルを指定して、
Program のボタンを押します。下に
Erasing device... OK
Programming Flash...OK
Verifying Flash...OK
が表示されたら書き込み完了です。
●
エラー表示
デフォルトではエラーの表示が解かり難いので、 Debug → Options → Project → Build and Run の画面で
MSBuild progect,, の選択を下図のように2個所とも Detailed に変更してOKします
●
lssファイル
時に、どのようにコンパイルされているのか、見たい時がありますが、下図のように Solution Explorer 画面の Output Files
のlss を開くとアセンブラで展開されている状態を確認することができます。
cソースに戻るには main.c をクリックします。
AVR プログラミングの例
1) ポートへの出力とループタイマー
割込みを使わないループタイマーの例です。
#include <asf.h>
#include <avr/io.h>
void lptm(unsigned int ms); //プロトタイプ宣言
int main (void)
{
DDRB = 0xff; // ポート入出力設定
IN=0 OUT=1
while(1)
{
PORTB = 0xff; //ポートB
の全ビットに1を出力
lptm(200); //200msタイマー
PORTB = 0; //ポートBの全ビットに0
を出力
lptm(200);
}
}
// 1msec×N ループタイマー
void lptm(unsigned int ms)
{
volatile int i;
while(ms)
{
for (i = 420; i > 0;
i--); // 8MHz
ms--;
}
return;
}
2) スイッチの信号を入力する
AVRの場合、出力ポートと入力ポートのレジスタは別になっているため、出力は PORTx を使いますが、入力する時は PINx を使います。
回路図1でAポートのb0を入力に設定し、ポートBは出力ポートに設定します。
Aポートのb0の入力を判断し、結果をBポートに出力しています。
#include <asf.h>
#include <avr/io.h>
#include <avr/interrupt.h>
int main (void)
{
// PORT IN=0 OUT=1
DDRA = 0b01111110; //PA0(40),,PA7(33)
PA0 = input
DDRB = 0xff; //PB0(1),,PB7(8)
PA7,,PA0 = output
while(1)
{
if(PINA & 0x01)
{
PORTB
= 0xff;
}
else
{
PORTB
= 0;
}
}
}
3) Timer0 CTC比較一致 割込み
Timer0 CompareMatch Interrupt
8bit TIMER0 を割込みで使用するタイマー
ISR(TIMER0_COMPA_vect) は加算カウンターが、OCR0Aと一致した時に発生する割込みです。
#include <asf.h>
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER0_COMPA_vect) //timer0割込み
{
PORTA ^= 0xff; //1ms毎にポート反転
PORTB ^= 0xff;
PORTC ^= 0xff;
PORTD ^= 0xff;
}
int main (void)
{
//8bit timer 0 割込み
TCCR0A = 0b10000010; //コンペアマッチ、.CTC動作
TCCR0B = 0b00000100; //クロック分周比:8MHz/256 ( 32us/count)
TIMSK0 = 0b00000010; // 割込みマスク:コンペアマッチ割込み
OCR0A = 32; //
時定数 32*32= 1ms
// PORT設定 IN=0 OUT=1
DDRA = 0xff;
DDRB = 0xff;
DDRC = 0xff;
DDRD = 0xff;
sei(); //
割込み開始
while(1)
{
}
TCCR0B のb2,b1,b0でクロックの分周比
b2 b1 b0
0 0 0 停止
0 0 1 ck (CPUクロック)
0 1 0 ck / 8
0 1 1 ck / 64
1 0 0 ck / 256
1 0 1 ck / 1024
1 1 0 外部T0(PD4)の立下り
1 1 1 外部T0(PD4)の立上り
4) Timer1 オーバーフロー割込み
-Timer1 overflow interrupt-
16bit TIMER1 の減算オーバーフローで発生する割込みのタイマーです。
TCNT1の値を変化させながら、ステッピングモーターを低速から高速に変化させるような用途に適しています。
#include <asf.h>
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER1_OVF_vect)
{
PORTA ^= 0xff; //1ms毎に反転
PORTB ^= 0xff;
PORTC ^= 0xff;
PORTD ^= 0xff;
}
int main (void)
{
// TMCNT1 16bitタイマー設定
TCCR1B = 0x02; // clock
(8M/8) = 1MHz (1us)
TIMSK1 = 0x01; // Overflow Interrupt Enable
TCNT1 = 1000; //
16ビットTimer時定数
// PORT setting IN=0 OUT=1
DDRA = 0xff;
DDRB = 0xff;
DDRC = 0xff;
DDRD = 0xff;
sei(); //
Interrupt
while(1)
{
}
}
5) ノイズ除去
ポートからの出力は簡単なのですが、入力のしかたは厄介です。例2のように INP して判断するだけでは正常な判断はできません。その第一の原因はノイズです。例えば、腕時計のように、金属ケースで覆われた電子回路は別ですが、通常の電子機器の回路には、外部の電界や磁界の影響を受け、1〜5μs
程度のスパイク状ノイズが頻繁に発生しているからです。このノイズを除去するためには、0.5〜1msの間隔で信号を入力し、連続して安定した信号だけを取り込みます。
なお、スイッチの接点がバウンドして発生するチャタリングは10〜20msですから、30msの間隔でタイマー取り込みするような手段をとります。
#include <asf.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <string.h>
#include <avr/pgmspace.h>
#define PAIO 0b10000010 // IN=0 OUT=1 (iPHP iINT iPS iDHP iTCL oTX iRX)
#define PALG 0b00000001 // 論理整合
#define PBLG 0b11111111 // 論理整合
#define TC0 (0xff - 4) // 8ビットタイマー 128μs×4 = 0.512ms
#define sw0 0b00000001 // スイッチ入力
//バッファ
volatile unsigned char PAinbo; // PD input buff old
volatile unsigned char PAinbn; // PD input buff new
volatile unsigned char PAinb; // PD input buff 確定
// 8ビットタイマー割込み
ISR(TIMER0_OVF_vect) //SIGNAL(SIG_OVERFLOW0) 8ビットタイマー0オーバーフロー割込み
{
TCNT0 = TC0; //間隔 0.5ms
// センサー入力
PAinbo = PAinbn; //
前回入力データを旧データにセーブ
PAinbn = PINA; //
ポートを新バッファに入力
PAinbn = PAinbn ^ PALG; // 論理整合
if (PAinbn == PAinbo) //
旧=新データなら
{ PAinb = PAinbn; } //
PDinbに格納
}
int main (void)
{
DDRA = PAIO; // A(b0)=input
DDRB = PBLG; // B out
// 8bitタイマー設定
// センサー監視入力
TCCR0B = 0b00000101; // タイマークロック (8M/1024) = 7.81KHz
(128μs)
TIMSK0 = 0x01; // Overflow Interrupt Enable
TCNT0 = TC0; // 時定数
sei();
while(1)
{
if(PAinb & sw0 )
{
PORTB
= 0xff;
}
else
{
PORTB
= 0;
}
}
}
★
Hint !
上の例に出てくる volatile は「揮発性取り扱い注意」をコンパイラに伝えています。なぜならコンパイラは、関連する前後で使用されない変数は冗長な記述とみなして、コンパイルを飛ばしてしまう可能性があります。割込みで取り込むバッファや、ポートのバッファなどを宣言する時は、この
volatile 宣言が欠かせません。
6) ステッピングモーター駆動
ステッピングモーターのパターンをタイマー割り込みで出力し、加減速制御する例です。
励磁パターンは1-2相モーターに合わせてください。
なお、ここに出てくる pgm_read_word(); は、フラッシュメモリ領域に格納されたデータを読み出してRAMを解放するためのマクロです。ヘッダファイル avr/pgmspace.h
が必要となります。
#include <asf.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
// ======== 定数 ========
#define MINI_SPEED 1000 // 最低速度
#define INI_SPEED 540 //500 デフォルト速度
#define MAX_SPEED 460 // 最高速度
#define STEP_INI 100 // TMの初期化ステップ数
#define L_SPEED 3000 // 最低限界速度
// ========== 外部変数 バッファ、フラグ =============
volatile unsigned int *speedpt; // 速度ポイン
volatile unsigned char out_patern; // 出力中パターン
volatile unsigned char mod; // 駆動モード (0:停止 1:加速 2:減速 3:ブレーキ)
volatile unsigned char rlf; // 回転方向フラグ
volatile signed char p_cnt; // 相カウンタ
volatile unsigned int max_speed; // TM最高速度バッファ
volatile unsigned char acc_cnt; // 加速ステップカウンタ
volatile unsigned int step_ct; // ステップカウンタ
volatile unsigned int speed_bf; // ステッピングモーター速度をセットするバッファ
volatile unsigned char stop_f; // 停止フラグ
// ======== IN/OUT port =========
// PA
#define TM 0x0f // (out) Tractor Motor
// 3 2 1 0
// INA /INA INB /INB SLA7033M
#define PAIO 0b11111111 // IN=0 OUT=1
// PB
#define PBIO 0b00001111 // IN=0 OUT=1
#define PBLG 0b11110000 // 論理整合
// PC
#define PCIO 0b11111100 // IN=0 OUT=1
// PD
#define PDIO 0b10000010 // IN=0 OUT=1 (iPHP iINT iPS iDHP iTCL oTX iRX)
#define PDLG 0b01011000 // 論理整合
// ========== プロトタイプ宣言 =============
// out
void outp(void);
// 一般
void ini(void);
void all_off(void);
void lptm(unsigned int);
void sectm(unsigned int);
//=========定数テーブル==================================
// 速度テーブル
PROGMEM const unsigned int speed_tbl[] =
{ // a=0.093 CF近似
3350,3038,2755,2499,2267,
2056,1865,1691,1534,1391,
1262,1144,1038,941,854,
774,702,637,578,524,
475,431,391,354,321,
291
};
// パターンテーブル
const char patern[] = {0x07,0x05,0x0d,0x09,0x0b,0x0a,0x0e,0x06}; //SLA7033M
//====================================
// timer-W 割り込み
// TMステッピングモーター駆動 // 間隔はspeed_tbl の時定数 500で0.5ms
ISR(TIMER1_OVF_vect)
{
// TMモーターの加減速制御
switch(mod)
{
case 0: // 励磁offモード
PORTA |= TM; // モーター出力off
break;
case 1: // 加速→定速 モード
speedpt++; //スピードポインタを上にシフト
if (*speedpt < max_speed)
//最高速度を越していたら
{
speedpt--;
// スピードポインタ戻して
TCNT1
= 0xffff - max_speed; // 最高速度を制限
}
else
{
TCNT1
= 0xffff - *speedpt; // 加速
acc_cnt++;
// 加速ステップをカウント
}
outp(); //パターン出力
break;
case 2: // 減速モード
speedpt--; //スピードポインタを下へシフト
if (speedpt <= speed_tbl)
//最低速度以下なら
{
speedpt
= speed_tbl;
mod
= 0; // 停止へ
}
else
{
TCNT1 = 0xffff - *speedpt;}
outp();
//パターン出力
break;
case 3: //ブレーキ保持
PORTA = out_patern;
break;
}
// ステップカウンタによる減速制御
if(step_ct > 0) //ステップ制御中なら
{
step_ct--; //カウンタ減算
if(!step_ct) // ステップカウンタが0になったら
{
mod
= 0; // モーターoffへ
}
if(step_ct - acc_cnt <=
0) //減速開始点に達したら
{
mod=2;
//減速モードに切り替え
}
}
}
/* *** ステッピングモーターにパターンを出力***** */
void outp(void)
{
if(rlf) // 右回転
{
PORTA &= ~TM; // 旧出力をクリヤして
PORTA |= out_patern = (*(patern
+ p_cnt) & TM); // 更新パターンを出力
p_cnt++; // 相カウンタをシフト
if (p_cnt>=8) // 相カウンタが最後まで行ったら
{ p_cnt = 0; } // リセット
}
else // 左回転
{
PORTA &= ~TM;
PORTA |= out_patern = (*(patern
+ p_cnt) & TM);
p_cnt--;
if (p_cnt < 0)
{ p_cnt = 7; }
}
}
/*
======================================
メイン
======================================
*/
int main(void)
{
lptm(200); // 電源安定まで待機
ini(); // デバイス初期化
sei(); // 割込有効
lptm(500); // 入力バッファ有効まで待機
// メインループ
max_speed = 900;
while(1)
{
mod = 1;
lptm(10000);
mod = 0;
lptm(100);
}
}
/*
==========================
初期化
==========================
*/
// デバイス初期化
void ini(void)
{
pgm_read_word(speed_tbl); // SRAM開放
// IOポート設定
DDRA = PAIO; // ポート入出力指定 IN=0 OUT=1
DDRB = PBIO;
DDRC = PCIO;
DDRD = PDIO;
all_off();
// TMCNT1 16bitタイマーカウンター1 設定
// TM制御
TCCR1B = 0x02; // clk (8M/8) = 1MHz (1us)
TIMSK1 = 0x01; // Overflow Interrupt Enable
TCNT1 = L_SPEED; // 16ビットTimer時定数
// 一般定数設定
mod =0;
speedpt = speed_tbl; //&speed_tbl[0]
stop_f = 0; // 停止ドフラグ
acc_cnt = 0; // 加速カウンタクリヤ
step_ct = 0;
// モーター速度
speed_bf = INI_SPEED; // 初期化時の速度に
}
/*
==========================
一般機能
==========================
*/
// 全off
void all_off(void)
{
PORTA = 0x0f;
PORTB = 0xff; // off output
PORTC = 0xff;
PORTD = 0xff;
}
// motor off (main用)
void m_off(void)
{
mod = 0;
PORTA |= TM; // TM 励磁電流 off
}
// 減速して停止
void m_stop(void)
{
mod = 2;
}
// 右回転
void m_right(void)
{
speedpt = speed_tbl; //スピードポインタ初期化
rlf = 0; // 右
mod = 1; // 回転
}
// 左回転
void m_left(void)
{
speedpt = speed_tbl; //スピードポインタ初期化
rlf = 1; // 右
mod = 1; // 回転
}
// 1msec×N ループタイマー
void lptm(unsigned int ms)
{
volatile int i;
while(ms)
{
for (i = 440; i > 0;
i--); // 8MHz
ms--;
}
return;
}
// 秒タイマー
void sectm(unsigned int sec)
{
volatile int i;
while(sec)
{
lptm(1000);
sec--;
}
}
注1)ステップモーターを等加速度で駆動する場合の speed_tbl[] 値は
加速度計算 を使って計算してみてください。