PIC32で自動運転
次は自動運転です。まずはブレッドボードでやってみます。
【3区間での自動往復運転】
・レールにギャップを入れて3つの区間に分け、それぞれの区間を別のドライブ回路で駆動します。両端の区間に差し掛かったことを検知して列車を減速、停止後に逆転し、この動作を繰り返します。


電源部に使用している4556はオーディオ用のOPアンプですが、出力電流が73mAと大きく、これでPIC32用電源3.3Vの仮想GNDを作ります。PIC32のGNDを12V電源のGNDから4.35V上げることにより、PICのPortの電圧の中心を6Vにしています。PortがInPortのときハイインピーダンスでOFF、OutPortで出力1のとき正転、0の時逆転します。共通側のRB12はその反対に正転で0、逆転で1にします。


PIC32のコード
前回と同様にMPLABでプロジェクトを作ります。名前は”3sections”にしました。
Harmonyの設定





・コードの編集前に文字コードをShift-Jisに変更します。
・3つのファイルをプロジェクトに追加します。
- atc.h
- timer_int.c
- lcd.c
コードのダウンロード 3sections
・まずヘッダーファイル’atc.h’を追加します。
作ったプロジェクトのあるフォルダー 3sections(プロジェクト名)-firmware-src のフォルダーに追加するファイルを入れておいて、Header Filesのappを右クリック、Add Existing Itemでプロジェクトに追加します。

・’timer_int.c’ と’lcd.c’ はSource Filesのappを右クリック、Add Existing Itemでプロジェクトに追加します。
または、New->xc32_header.h…で新規に作成し、名前を’atc.h’にします。出来たコードをすべて削除し、以下のコードをコピペします。’timer_int.c’と’lcd.c’も同様にNew->xc32_newfile.cで作成します。
atc.h
//I2C用変数
DRV_HANDLE hi2c;
DRV_I2C_BUFFER_HANDLE bhi2c;
//16bitのINT型の上位H byte、下位L byte読み込み用共用体
union byte_access {
int INT; // Int Access
struct { // byte Access
unsigned char L;
unsigned char H;
} BYTE;
};
//位置情報の構造体
struct PSI_BIT {
unsigned char KUKAN : 3; /* 区間 Bit 0-2 */
unsigned char ROSEN : 3; /* 路線 Bit 3-5 */
unsigned char SOU : 1; /* 相 Bit 6 */
unsigned char DIR : 1; /* 方向 Bit 7 */
};
struct STATUS_BIT {
unsigned char CHG : 1; //区間変化
unsigned char REV : 1; //逆転
unsigned char DUMY : 4; //DUMY
unsigned char SLOW : 1; //減速
unsigned char SAFE : 1; //安全
};
//列車の位置情報の構造体
struct st_position {
//現在位置
union { /* Position */
unsigned char BYTE; /* Byte Access */
struct PSI_BIT BIT; /* Bit Access */
} NOW;
//前の位置
union { /* Position */
unsigned char BYTE; /* Byte Access */
struct PSI_BIT BIT; /* Bit Access */
} BEFORE;
//次の位置
union { /* Position */
unsigned char BYTE; /* Byte Access */
struct PSI_BIT BIT; /* Bit Access */
} NEXT;
//次々の位置
union { /* Position */
unsigned char BYTE; /* Byte Access */
struct PSI_BIT BIT; /* Bit Access */
} ANEXT;
union {
unsigned char BYTE; /* Byte Access */
struct STATUS_BIT BIT; /* Bit Access */
} STATUS;
int speed;
//int speed_cont;
union byte_access speed_cont;
int speed_err;
int speed_peak;
int mascon;
unsigned char henka;
//unsigned char safe;
union byte_access speed_ret; //読み取りスピード
union byte_access speed_ret_rx; //読み取りスピード
union byte_access speed_ret_tx; //読み取りスピード
unsigned char point;
unsigned char yard;
union byte_access speed_rx;
};
//監視区間設定用の共用体
union scan_port {
unsigned char BYTE; /* Byte Access */
struct {
unsigned char DUMMY : 2; /* Bit 0-1 */
unsigned char KUKAN : 3; /* Bit 2-4 監視区間*/
unsigned char DISABLE : 1; /* Bit 5 */
unsigned char B6 : 1; /* Bit 6 */
} BIT;
};
#define TR_COUNT 1 //列車数
#define ROSEN_NUM 0 //路線(ボード)番号
unsigned char EnableBit[] = {1, 2, 4, 8, 16, 32, 64, 128};
unsigned int int_counter; //インタラプトカウンタ
unsigned int speed_pw[2]; //スピードパルス幅
struct st_position train[TR_COUNT]; //列車位置情報
struct st_position* train_sou[2];
unsigned char cont_train[2]; //制御する列車番号
unsigned char kukan[2]; //区間
unsigned char kanshi_now[2]; //スピード読み込み用
unsigned char kanshi_before[2]; //スピード読み込み用
union scan_port kanshi; //区間監視用
union byte_access adc_ret; //ADC値
union byte_access debug1; //ADC値
timer_int.c
#include "system/common/sys_common.h"
#include "app.h"
#include "system_definitions.h"
void kukan_ON(unsigned char, unsigned char);
void kukan_OFF(unsigned char);
void speed_pulse_on(unsigned char);
void speed_pulse_off();
void section_change_monitor(unsigned char); //区間変化監視
void next_section_on(unsigned char); //次区間ON
void timer_int_func() {
//インタラプトを0-0x1ffでカウント
if (++int_counter > 0x1ff) {
int_counter = 0;
}
switch (int_counter) {
case 0:
//インタラプトカウンタが0のときA相のspeedパルスON
train_sou[0] = &train[cont_train[0]];
speed_pulse_on(0);
break;
case 2:
//インタラプトカウンタが0x2のときA相の区間変化監視(B相は0x102のとき)
section_change_monitor(0);
break;
case 3:
//インタラプトカウンタが3のときNEXT区間パルスON(B相は0x103のとき)
//区間変化確認後に次区間もON
next_section_on(0);
break;
}
//スピードパルスOFF
speed_pulse_off();
}
void speed_pulse_on(unsigned char sou) {
if (train_sou[sou]) {
kukan[sou] = 0; //kukan clr
speed_pw[sou] = train_sou[sou]->speed;
//区間メモリに現・前位置の区間を設定し区間ON
if (train_sou[sou]->NOW.BIT.ROSEN == ROSEN_NUM) {
kukan[sou] = EnableBit[train_sou[sou]->NOW.BIT.KUKAN];
kanshi_now[sou] = train_sou[sou]->NOW.BIT.KUKAN;
}
if (train_sou[sou]->BEFORE.BIT.ROSEN == ROSEN_NUM) {
kukan[sou] |= EnableBit[train_sou[sou]->BEFORE.BIT.KUKAN];
kanshi_before[sou] = train_sou[sou]->BEFORE.BIT.KUKAN;
}
// 区間ON
kukan_ON(kukan[sou], train_sou[sou]->NOW.BIT.DIR);
}
}
//区間ON用
void kukan_ON(unsigned char kukan, unsigned char d) {
// d=0:正回転, 1:負回転
if (d == 0) {
LATB |= kukan;
_LATB12 = 0; //Port RB12は共通側
} else {
LATB &= ~kukan;
_LATB12 = 1;
}
TRISB &= ~kukan;
_TRISB12 = 0;
}
//区間OFF用
void kukan_OFF(unsigned char kukan) {
TRISB |= kukan;
_TRISB12 = 1;
}
void speed_pulse_off() {
//インタラプトごとにspeedをマイナス1して0になったらパルスOFF
unsigned char n;
for (n = 0; n < 2; n++) {
if (train_sou[n]) {
if (speed_pw[n] == 0) {
//speedが0で区間パルスOFF
kukan_OFF(kukan[n]);
} else {
speed_pw[n]--; //speedをマイナス1
}
}
}
}
void section_change_monitor(unsigned char sou) {
if (train_sou[sou]) {
if (train_sou[sou]->STATUS.BIT.SAFE)
if (!train_sou[sou]->henka) {
if (train_sou[sou]->NEXT.BIT.ROSEN == ROSEN_NUM) {
//次位置区間の監視
kanshi.BIT.KUKAN = train_sou[sou]->NEXT.BIT.KUKAN;
LATA &= 0xC3;
LATA |= kanshi.BYTE;
//ADC読み込み
PLIB_ADC_SamplingStart(ADC_ID_1);
while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1));
adc_ret.INT = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0);
if (sou == 0)debug1.INT = adc_ret.INT;
if (train_sou[sou]->NOW.BIT.DIR == 0) {
//正方向のとき
if (adc_ret.BYTE.H >= 0x3) {
train_sou[sou]->henka = 0x80; //区間変化
}
} else {
//逆方向のとき
if (adc_ret.BYTE.H < 0x1) {
train_sou[sou]->henka = 0x80; //区間変化
}
}
}
}
}
}
void next_section_on(unsigned char sou) {
//次区間をON
if (train_sou[sou]) {
if (train_sou[sou]->STATUS.BIT.SAFE) {
//安全でないときは次区間に他の列車がかかっているのでONしない。
if (train_sou[sou]->NEXT.BIT.ROSEN == ROSEN_NUM)
kukan[sou] |= EnableBit[train_sou[sou]->NEXT.BIT.KUKAN];
kukan_ON(kukan[sou], train_sou[sou]->NOW.BIT.DIR);
}
}
}
/* *****************************************************************************
End of File
*/
lcd.c
#include "system/common/sys_common.h"
#include "app.h"
#include "system_definitions.h"
#define ADR 0x7C
void LCD_cmd(char);
void LCD_int(void);
void LCD_str(char *);
void LCD_dat(char);
void LCD_hex(char);
void LCD_posyx(char, char);
void BSP_DelayMs(unsigned short);
void BSP_DelayUs(unsigned short);
uint8_t data[5];
//遅延調整用関数(ms)
void BSP_DelayMs(unsigned short milliseconds) {
unsigned long time;
time = _CP0_GET_COUNT(); //Read Core Timer
time += (SYS_CLK_FREQ / 2 / 1000) * milliseconds; //calc the Stop Time
while ((long) (time - _CP0_GET_COUNT()) > 0) {
};
}
//遅延調整用関数(μs)
void BSP_DelayUs(unsigned short microseconds) {
unsigned long time;
time = _CP0_GET_COUNT(); //Read Core Timer
time += (SYS_CLK_FREQ / 2 / 1000000) * microseconds; //calc the Stop Time
while ((long) (time - _CP0_GET_COUNT()) > 0) {
};
}
void LCD_cmd(char cmd) {
data[0] = 0x80;
data[1] = cmd;
bhi2c = DRV_I2C_Transmit(hi2c, ADR, data, 2, NULL);//I2C送信
while (DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE); /*I2C完了待ち*/
if (cmd & 0xFC) // LCDのコマンドにより待ち時間が違う
BSP_DelayUs(60); // 60usec
else
BSP_DelayMs(3); // 3msec
}
void LCD_int(void) {
BSP_DelayMs(100);
LCD_cmd(0x38);
LCD_cmd(0x39);
LCD_cmd(0x14);
LCD_cmd(0x73); //7A(forAQM1602)
LCD_cmd(0x56); //54(forAQM1602)
LCD_cmd(0x6C);
BSP_DelayMs(200);
LCD_cmd(0x0C); //Disp ON/OFF
LCD_cmd(0x01);// Clear Display
BSP_DelayUs(1100);
}
//文字列表示
void LCD_str(char *str) {
while (*str) //0x00まで繰り返し
LCD_dat(*str++); //1文字表示
}
//1文字表示
void LCD_dat(char chr) {
data[0] = 0x40;
data[1] = chr;
//I2C送信
bhi2c = DRV_I2C_Transmit(hi2c, ADR, data, 2, NULL);
while (DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE);
BSP_DelayUs(60); // 60usec
}
//-------- 16進文字変換表示 ----------------
void LCD_hex(char c) {
const char hexch[] = "0123456789ABCDEF";
LCD_dat(hexch[c >> 4]); //上位4bit表示
LCD_dat(hexch[c & 0xF]); //下位4bit表示
}
//--------3桁 10進文字表示 ----------------
void LCD_dec(int i) {
const char decch[] = "0123456789 ";
char n[3];
n[2] = i / 100;
n[1] = (i % 100) / 10;
n[0] = i % 10;
if (n[2] == 0){
n[2] = 10;//100の桁は0ならスペース
if (n[1] == 0){
n[1] = 10;//100の桁0で10の桁0ならスペース
}
}
LCD_dat(decch[n[2]]);
LCD_dat(decch[n[1]]);
LCD_dat(decch[n[0]]);
}
//-------- カーソル位置指定 --------------------------------------
void LCD_posyx(char ypos, char xpos) {
unsigned char pcode;
switch (ypos & 0x03) { // 縦位置を取得
case 0: pcode = 0x80;
break; // 1行目
case 1: pcode = 0xC0;
break; // 2行目
case 2: pcode = 0x94;
break; // 3行目
case 3: pcode = 0xD4;
break; // 4行目
}
LCD_cmd(pcode += xpos); // 横位置を加える
}
/*******************************************************************************
End of File
*/
・次に
‘app.h’
‘app.c’
‘system_interrupt.c’
を編集します。
app.hの編集
#include “atc.h”を追加します。
// ***************************************************************************** // ***************************************************************************** // Section: Included Files // ***************************************************************************** // ***************************************************************************** #include <stdint.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> #include "system_config.h" #include "system_definitions.h" #include "atc.h"//この行を追加 // DOM-IGNORE-BEGIN
app.cの編集
void APP_Tasks ( void )以下の部分をすべて以下のコードにすげ替えれば大丈夫だと思います。
/******************************************************************************
Function:
void APP_Tasks ( void )
Remarks:
See prototype in app.h.
*/
void section_chg(unsigned char);
void reverse(unsigned char);
void accel(char, int);
int slow_down(char);
int stoppage_time(int);
void APP_Tasks(void) {
/* Check the application's current state. */
switch (appData.state) {
/* Application's initial state. */
case APP_STATE_INIT:
{
bool appInitialized = true;
DRV_TMR0_Start(); //TimerStart
DRV_ADC_Open(); //Enable ADC
//I2C Open
hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if (hi2c != DRV_HANDLE_INVALID) {
//LCD初期化
LCD_int();
LCD_str("3section");
}
//-----ATC-------
cont_train[0] = 0;
cont_train[1] = 1;
train_sou[0] = NULL;
train_sou[1] = NULL;
//列車A相
train[0].speed = 0x0;
train[0].mascon = 0x80; //スピードを変える時はここを変更
train[0].NOW.BYTE = 3; //始めの列車位置は区間3
train[0].BEFORE.BYTE = 2;
train[0].NEXT.BYTE = 4;
train[0].ANEXT.BYTE = 5;
train[0].henka = 0;
train[0].STATUS.BIT.SAFE = 1;
if (appInitialized) {
appData.state = APP_STATE_SERVICE_TASKS;
}
break;
}
case APP_STATE_SERVICE_TASKS:
{
BSP_DelayMs(20);
LCD_posyx(1, 0);
//LCD_hex(debug1.BYTE.H);
//LCD_hex(debug1.BYTE.L);
LCD_str("N");
LCD_hex(train[0].NOW.BYTE);
LCD_str(" X");
LCD_hex(train[0].NEXT.BYTE);
//区間変化
char n;
for (n = 0; n < TR_COUNT; n++) {
if (train[n].henka == 0x80) {
train[n].henka = 0;
//区間が変わったら区間変更し、逆転
section_chg(n);
train[n].STATUS.BIT.REV = 1; //逆転
}
}
for (n = 0; n < TR_COUNT; n++) {
if (train[n].NOW.BYTE != 0x38) {
if (train[n].STATUS.BIT.REV == 1) {
//逆転命令
//減速
if (slow_down(n) == 0) {
//完全停止
if (stoppage_time(100) == 0) {
//一時停車後逆転
reverse(n);
}
}
} else {
if (train[n].STATUS.BIT.SAFE == 1) {
//安全
if (train[n].STATUS.BIT.SLOW == 1) {
//SLOW
if (train[n].mascon > 2) {
train[n].speed = 2;
} else {
train[n].speed = train[n].mascon;
}
} else {
//加速
accel(n, train[n].mascon);
}
} else {
//停止
train[n].speed = 0;
}
}
}
}
break;
}
/* TODO: implement your application state machine.*/
/* The default state should never be executed. */
default:
{
/* TODO: Handle error in application's state machine. */
break;
}
}
}
int slow_down(char n) {
if (train[n].speed > 0)
train[n].speed--;
return train[n].speed;
}
void accel(char n, int speed) {
if (train[n].speed < speed)
train[n].speed++;
}
void section_chg(unsigned char n) {
train[n].BEFORE.BYTE = train[n].NOW.BYTE;
train[n].NOW.BYTE = train[n].NEXT.BYTE;
train[n].NEXT.BYTE = train[n].ANEXT.BYTE;
if (train[n].NOW.BIT.DIR) {
train[n].ANEXT.BIT.KUKAN--; //逆転
} else {
train[n].ANEXT.BIT.KUKAN++; //正転
}
//
train[n].STATUS.BIT.CHG = 1;
}
void reverse(unsigned char n) {
unsigned char before;
unsigned char dir = train[n].NOW.BIT.DIR;
if (dir) {
dir = 0;
} else {
dir = 1;
}
train[n].NOW.BIT.DIR = dir;
train[n].BEFORE.BIT.DIR = dir;
before = train[n].BEFORE.BYTE;
train[n].BEFORE.BYTE = train[n].NOW.BYTE;
train[n].NOW.BYTE = before;
train[n].NEXT.BYTE = before;
train[n].ANEXT.BYTE = before;
if (dir) {
//逆転
train[n].NEXT.BIT.KUKAN--;
train[n].ANEXT.BIT.KUKAN = train[n].NOW.BIT.KUKAN - 2;
} else {
//正転
train[n].NEXT.BIT.KUKAN++;
train[n].ANEXT.BIT.KUKAN += 2;
}
train[n].STATUS.BIT.REV = 0;
}
int stoppage_time(int t) {
//停車時間
static int timer;
if (timer-- == 0)
timer = t; //停車時間
return timer;
}
/*******************************************************************************
End of File
*/
system_interrupt.cの編集
以下の部分に
timer_int_func();
の一行を追加します。
void __ISR(_TIMER_1_VECTOR, ipl7AUTO) IntHandlerDrvTmrInstance0(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_TIMER_1);
timer_int_func();
}
以上でコードは完成です。PicKitで書き込みましょう。
