【かんたん!マイコン工作
2016
】
PIC12F1822
マイコンプログラミング演習
1
マイコン基板の製作
(1) 回路図
(2) 実体配線図
サンハヤト製ユニバーサル基板ICB-91を使った配線例を下図に示す。
A
PIC 12F1822
A 凹 黒 FirmwareCode
(Universal Circuit Board Sunhayato ICB-91)
PIC 12F1822
E
凹
E
104
Opt-Transistor NJL-7502 LED
2
書き込み用ハードウェアの用意
(1) Microchip製 PICkit3PICマイコンを製造しているMicrochip社純正のPICライタである。
USBケーブルでパソコン( 対応OS : OS X , Linux , Windows ) と 接続し、後で説明する統合開発環境を使ってマイコンにプログラムを書き 込むことができる。
(2) ジャンパーコード(5本) 、ブレッドボード、電池ボックス(単3電池×3本)
ICテストクリップの代わりに、ブレッドボードにマイコンを挿入し、ジャンパコードでマイ
コンとPICライタを接続することもできる。
3
ソフトウェアのインストール
マイコンのプログラム開発に必要なソフトウェアは、プログラムのソースコードを入力したり、
管理したりする「統合開発環境
(IDE)
とソースコードをマイコンの機械語に変換するコンパイラや リンカなどのツール群である。(1) MPLAB XIDE
PIC
マイコンのプログラムを開発する 統合開発環境のソフトウェアを以下のURL
からダウン ロードしてインストールする。http://www.microchip.com/mplab/mplab-x-ide
”
MPLAB XIDE”
のアイコンからダウンロードしたファイルを展開してから実行し、指示に従ってインストールする。
(2) XC8 Compiler
C
言語のソースコードを機械語に変換するC
コンパイラをダウンロードしてインストールする。http://www.microchip.com/mplab/compilers
MPLAB XC8 Compiler
のアイコンからダウンロードしたファイルを展開してから実行し、指示4
PICkit3
とマイコンを接続
(1) 専用書き込みケーブルを自作して使う場合
PICライタとマイコンを接続するには、マイコン
の脇につけられたピンソケットにジャンパコードを 挿入して接続すればよいが、写真に示したような接 続用のケーブルを製作して接続すると間違いがない。
(2) ブレッドボードを使う場合
ブレッドボードにマイコンを挿入し、ジャンパコードでマイコンと
PIC
ライタを接続することもできる。
右の写真は、接続した様子である。
下図は
PIC12F1822
の機能ピンと接続の関係を表したものである。
PICマイコンの電源端子VDDには、電源(単3電池
3本)のプラス端子をつなぎ、VSSにはマイナス端
子をつなぐ。
PICkit3
PIC
12F1822 VSS Ground
VDD
5
ファームウェア(実行可能プログラム)の作成とマイコンへの書き込み
(1) MPLAB XIDEを起動(右のアイコンはMPLAB XIDE ver. 3.26のもの)
(2) プロジェクト(Project)の作成
(a)メニューから[File]-[New Project...]をクリック (b) [Choose Project]ダイアログボックス
[Next>]をクリック。
(c) [Select Device]ダイアログボックス
[Family]は[Mid-range 8-bit MCUs (PIC12/10/16/MCP)]を 選択
[Device]は[PIC12F1822]を選択。 (マイコンによって変更)
[Next>]をクリック
(d) [Select Header]ダイアログボックス [Next>]をクリック
(e) [Select Tool]ダイアログボックス [PICkit3]を選択
[Next>]をクリック
(f) [Select Compiler]ダイアログボックス
[XC8]の中にある[XC8 (v*.**)]を選択(v*.**)はバージョンに よって異なる。
(g) [Select Project Name and Folder]ダイアログボックス [Project Name]にプロジェクト名
(ここでは[led-12f1822])を入力。
[Encoding]に[UTF-8]を選択(日本語のコメントが入力可能)
[Finish]をクリック
(3) ソースファイルの追加
(a) XIDEの窓の左側
[Projects]タブ内の[led-12f1822]の下位にある[Source Files]をクリックして選 択。
(b) メニューから[File]-[New File...]をクリック
(c) [Choose File Type]ダイアログボックス
[Microchip Embedded]を開き、[XC8 Compiler]を選択、更に[main.c]を選択。 [Next>]をクリック
(d) [Name and Location]ダイアログボックス
(4) ソースファイルの入力
X IDE右側のソースファイルのタブに、ソースコードを入力していく。
(例) LEDを点滅させるコード。いわゆる「Lチカ」の最低限のコード
#include <xc.h> #include <stdint.h>
#pragma config FOSC = INTOSC #pragma config WDTE = OFF #pragma config PWRTE = ON #pragma config MCLRE = OFF #pragma config CP = OFF #pragma config CPD = OFF #pragma config BOREN = ON #pragma config CLKOUTEN = OFF #pragma config IESO = OFF #pragma config FCMEN = OFF
#pragma config WRT = OFF #pragma config PLLEN = OFF #pragma config STVREN = ON #pragma config BORV = HI #pragma config LVP = OFF
void main(void) {
volatile uint32_t i;
OSCCON = 0b01110010 ; OPTION_REG = 0b00010000 ; ANSELA = 0b00010000 ; TRISA = 0b00011000 ; WPUA = 0b00001000 ; PORTA = 0b00000000 ;
while (1) { RA0 ^= 1;
for(i = 0; i < 10000; i++); }
(5) ビルドと書き込み
[Make and Program Device Main Project]のアイコンをクリック
[CAUTION]警告のダイアログボックスが表示されたら、
電源が正しく接続されていることを確認して、[OK]をクリック。
※書き込みの段階で、赤い字でエラーメッセージが出る場合、電源が接続されていなかったり、 クリップやジャンパーコードの接続に不具合があったりすることが多い。
6
プログラム作成演習
以下の演習では、プログラムの先頭部分に、下記の共通部分を必ず配置する。 /* 共通部分 開始 */
/*
* LED1 : PIN 7 (RA0) : 1-ON, 0-off * ANALOG PORT : PIN 3 (AN3) */
#include <xc.h>
#include <stdint.h> // ビットを意識した int 型を使うためのヘッダファイル
// LEDを光らせるマクロを定義
#define ledOn() (RA0 = 1) // LED点灯 #define ledOff() (RA0 = 0) // LED消灯
#define led() (RA0 ^= 1) // LEDを点滅する
// PIC12F1822のコンフィグレーション (Configuration) #1 #pragma config FOSC = INTOSC // Internal Clock #pragma config WDTE = OFF // NO Watchdog timer
#pragma config PWRTE = ON // Program start at Power on after 64ms #pragma config MCLRE = OFF // No Ext Reset, Enable RA3 input pin #pragma config CP = OFF // No Code protect
#pragma config CPD = OFF // No Data Protect #pragma config BOREN = ON // Brown out enable #pragma config CLKOUTEN = OFF // CLKOUT as RA4
#pragma config IESO = OFF // No Internal External clock #pragma config FCMEN = OFF // No FCM
// PIC12F1822のコンフィグレーション (Configuration) #2 #pragma config WRT = OFF // No Write Protect #pragma config PLLEN = OFF // No PLL
#pragma config STVREN = ON // Reset on Stack over / under flow #pragma config BORV = HI // Watch Voltage drop (2.5V = HI) #pragma config LVP = OFF // No Low Voltage Programming
void wait(volatile uint32_t i)// 処理をちょっと待つ関数 {
while (i-- > 0); }
void initIO(void) // I/Oポートの初期化 {
OSCCON = 0b01110010 ; // Internal clock 8MHz
OPTION_REG = 0b00010000 ; // Weak Pull up Resistor RA4-OFF, others ON ANSELA = 0b00010000 ; // Analog PORT AN3(RA4)
TRISA = 0b00011000 ; // Input : RA34, Output: others WPUA = 0b00001000 ; // Weak pullup : RA3
PORTA = 0b00000000 ; // Initialize PORTA }
(1) LEDを一定間隔で点滅させるプログラム(前の例に注釈を入れ、共通部分を追加して変更)
/* 共通部分 開始*/ (略)
/* 共通部分 終了*/
void main(void) {
initIO(); // I/O ポートの初期化
while (1) { // 無限ループ
led(); // LED が点灯していれば消灯し、消灯していれば点灯する wait(10000); // ちょっと待つ
} return; }
(2) ADC(アナログ・ディジタル変換)を使ったプログラム。
(a)次のプログラムでLEDが点灯するのは周囲が明るいときか?暗いときか。
プログラムの動作を確認せよ。 /* 共通部分 開始*/
(略)
/* 共通部分 終了*/
void initAdc8(void)
// A/D 変換器(ADC)の初期設定 {
ANSELA = 0b00010000; //RA4 Analog port, others digital port ADCON0 = 0b00001101; //0:unimp, 00011:AN3, 0:GOnDone, 1:ADC enable
ADCON1 = 0b01000000; //0:Left justified (8bit res.), 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF
CM1CON0 = 0x00; //Comparator disable }
uint8_t getAdc8(void)
// A/D変換器(ADC)の値を返す。 {
uint8_t d; //ADC wait(10); GO_nDONE = 1; while (GO_nDONE); d = ADRESH; return d; }
void main(void) {
uint32_t t;
uint8_t adc_value;
initIO(); initAdc8();
while (1) {
adc_value = getAdc8(); // 光センサの状態を ADC で読み取る if (adc_value < 220) {
// 光センサの値が 220 より小さい(=暗い)とき ledOff();
} else { ledOn(); }
} return; }
課題 光センサの感度を変えるにはどこの数値をどのように変えればよいか もっと明るくても反応する場合…
(b)次のプログラムを実行し、光センサの周囲の明るさを変えたときに、LEDの光り方がどうなるか調べよ。
/* 共通部分 開始*/ (略)
/* 共通部分 終了*/
void initAdc8(void) {
ANSELA = 0b00010000; //RA4 Analog port, others digital port ADCON0 = 0b00001101; //0:unimp, 00011:AN3, 0:GOnDone, 1:ADC enable
ADCON1 = 0b01000000; //0:Left justified (8bit res.), 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF
CM1CON0 = 0x00; //Comparator disable }
uint8_t getAdc8(void) {
uint8_t d; //ADC wait(10); GO_nDONE = 1; while (GO_nDONE); d = ADRESH; return d; }
void main(void) { uint32_t t;
uint8_t adc_value;
initIO(); initAdc8();
while (1) {
adc_value = getAdc8(); // 光センサの状態を ADC で読み取る ledOn();
wait((uint32_t)adc_value * 100); ledOff();
wait((uint32_t)adc_value * 100); }
(3) タイマを使ったプログラム
3秒間でLEDを点灯し、1秒間LEDを消灯させるプログラム。 /* 共通部分 開始*/
(略)
/* 共通部分 終了*/
void initTimer0(void) {
OPTION_REG &= 0b11111000; // Clear Prescale parameter OPTION_REG |= 0b00000111; // Prescaler 1:256
TMR0 = 0; // initialize Timer0
TMR0IF = 0; // Timer0 interrupt enable TMR0IE = 1; // Enable Timer0 Interrupt GIE = 1; // Enable Interrupt
}
/*
* 1 interrupt
* => 1/8 * 4 (instruction exec clock) * 256(prescaler) * 256 = 32768 us , * 1 sec = 1 / 32768e-6 = 30.517 counts
*/
static volatile unsigned int T0count; #define TIMER0_1SEC 30
void interrupt InterFunction( void ) {
// Timer0 Interrupt if (TMR0IF == 1) { TMR0 = 0; T0count++; TMR0IF = 0; }
}
void main(void) { uint32_t t; uint8_t adc_value; initIO(); initTimer0();
while (1) {
ledOn(); // LED を点灯
for (T0count = 0; T0count < TIMER0_1SEC * 3; );
// タイマカウンタの値が1秒分(TIMER0_1SEC)の 3 倍を超えないうちは待つ。 ledOff(); // LED を消灯
for (T0count = 0; T0count < TIMER0_1SEC; );
// タイマカウンタの値が1秒分(TIMER0_1SEC)を超えないうちは待つ。 }
(4) 次のような状態遷移をするプログラムの動作を確認せよ。
/* 共通部分 開始*/ (略)
/* 共通部分 終了*/
enum STATUS {ST_STANDBY , ST_TIMER, ST_TIMEOUT} Status;
void initAdc8(void) {
ANSELA = 0b00010000; //RA4 Analog port, others digital port ADCON0 = 0b00001101; //0:unimp, 00011:AN3, 0:GOnDone, 1:ADC enable
ADCON1 = 0b01000000; //0:Left justified (8bit res.), 100:Fosc/4 ,0:Reserved, 00:AVDD=VREF CM1CON0 = 0x00; //Comparator disable
}
uint8_t getAdc8(void) {
uint8_t d; //ADC wait(10); GO_nDONE = 1; while (GO_nDONE); d = ADRESH; return d; }
void initTimer0(void) {
OPTION_REG &= 0b11111000; // Clear Prescale parameter OPTION_REG |= 0b00000111; // Prescaler 1:256
ST_STANDBY
待機状態
LED消灯
ST_TIMER
時間を測る
LEDを点滅
ST_TIMEOUT
LEDを消灯
光センサで
暗さを検出
光センサで
明るいと判断
規定時間経過
#define TIMER0_1SEC 30
void interrupt InterFunction( void ) {
// Timer0 Interrupt if (TMR0IF == 1) { TMR0 = 0; T0count++; TMR0IF = 0; }
}
void main(void) { uint32_t t; uint8_t adc_value; initIO(); initAdc8(); initTimer0();
Status = ST_STANDBY; T0count = 0;
while (1) {
switch (Status) { case ST_STANDBY :
adc_value = getAdc8(); if (adc_value < 220) { ledOff();
} else {
Status = ST_TIMER; T0count = 0; }
break; case ST_TIMER : led();
wait(185000L - T0count * 100L); adc_value = getAdc8();
if (adc_value < 220) { ledOff();
Status = ST_STANDBY; }
if (T0count > TIMER0_1SEC * 60) { Status = ST_TIMEOUT;
} break;
case ST_TIMEOUT : ledOff();
adc_value = getAdc8(); if (adc_value < 220) { Status = ST_STANDBY; }
break; default :
Status = ST_STANDBY; }