PIC16F1937でライントレーサを作ってみる

まえがき

やりたいこと

PICマイコンを使ってライントレーサを作っていこうと思います。PICマイコン, フォトリフレクタ, モータドライバを使って1から(キットではないという意味で)組み立てていく感じです。

作りたいと思った理由

今年の大学の前期でライントレーサのプログラムを作る というグループ課題があり、優勝(普通のトラックコースを複数あるグループの中でもっとも短い時間で走り終える かつ それよりも完走するのが難しいコースを1分以内にクリアする。そしてこれらは同じプログラムでなければならないという条件)してやろうかと挑んだわけですが普通のトラックコースでは最速ではなく2位(だったかな。1位のグループには発想で負けました), 難しいコースでは本番でコースを外れてしまうという失態を起こしてしまうという残念な結果に。せめてどっちかは完璧にしたかったんですが…。
と、悔しい思いをしてライントレーサをもうちょっとやりたいと思い、ならPICマイコンから作ってやろうと思った次第です。

製作にあたっての情報

使用するもの

(特別に必要なもので一般的なタクトスイッチや抵抗等は含まれていません)
– PIC12F683, PIC16F1937(PIC12F683は各部の実験用で使います。 PIC16F1937はライントレーサ本体で使います。) | 秋月電子通商, 秋月電子通商
– モータドライバ : TA7291P | 秋月電子通商
– フォトリフレクタ : LBR-127HLD | 秋月電子通商
– モータ(ギアボックス込み) : 楽しい工作シリーズ(ユニット) No.97 ツインモーターギヤーボックス | Amazon

環境

  • MacBook Pro (Retina, 13-inch, Early 2015) : macOS Sierra 10.12.5
  • MPLAB X IDE v3.6
  • MPLAB XC8 Compiler v1.42 (Free)

本題

Index

  • Part 1 : フォトリフレクタを使ってみる実験
  • Part 2 : PICマイコンでA/D変換をしてみる実験
  • Part 3 : モータドライバを使ってみる実験
  • Part 4 : ライントレーサを作る

Part 1 : フォトリフレクタを使ってみる実験

フォトリフレクタとは

フォトリフレクタは物体を検知するセンサの一つです。
外観はLEDを二つプラスチックの箱で囲ったようなものになっています。実際には片方は赤外線LEDでもう片方がフォトトランジスタです。
片方の赤外線LEDから出力された光が物体に反射してセンサがその光を受取る仕組みになっています。
赤外線の反射率で出力される値が変化するようになっていてライントレーサの場合、車体がライン上にあるのかそうでないのかを検知する”目”の役割を果たします。

実験

実験手順

白い紙のうち半分を黒く塗り図1の回路図の回路を組みセンサからの出力電圧を測ります。

図1 : フォトリフレクタ回路図

回路写真

図2 : 組んだ回路の写真

実験結果

OLYMPUS DIGITAL CAMERA

図3 : 白を検知した時

OLYMPUS DIGITAL CAMERA

図4 : 黒を検知した時

実験まとめ

実験結果より白を検知した時はセンサから得られる電圧値が低く、黒を検知した時はセンサから得られる電圧値が高いことがわかります。
単に色だけではなく、検知する物体との距離でも値が変わってしまうのでライントレーサで使用する場合コースはできるだけ平坦にする必要があります。
ライントレーサで使うときにはこのセンサの値(電圧値)をPICで読み込んでモータの回転数を上げたり下げたり…と制御して使います。

Part 2 : PICマイコンでA/D変換をしてみる実験

A/D変換とは

A/D変換はAnalog-Digital変換という意味です。(念のため…)
PICにはフォトリフレクタから得られたAnalog(連続的)な電圧値をDigital(離散的)な数値に変換してセンサの値を解釈し、モータの回転数制御に使おうというものです。

実験

実験手順

図5の回路を組み、PIC12F683用のプログラムを組んでLEDの光り方を見てます。

AD変換_回路図

図5 : AD変換回路図

8ビットシフトレジスタの使い方は前記事(PIC12F683 / PIC16F1937を使ってみる実験的な)に書いてあります。

次はプログラムです。

/*
 * File:   adconvtest.c
 * Author: BEAN
 *
 * Created on August 24, 2017, 9:13 PM
 */

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)

#include 

//__delay_ms()関数のためにクロック指定
#define _XTAL_FREQ 1000000

//各ピン名称設定
#define RCLK GP1    //ストアするクロックピン
#define SER GP2     //データ送るピン
#define SRCLR GP4   //シフトレジスタのクリアピン Lowでクリア
#define SRCLK GP5   //SERから読み取るクロックピン

void sendLedData(const unsigned char *, signed char);
unsigned int adconv();

void main(void) {

    // PIC初期化
    OSCCON = 0b01000000;    //1MHz駆動
    ANSEL  = 0b00000001;    //AN0(GP0)はアナログ入力ピン, 他はデジタルI/O, 変換時間は2us
    TRISIO = 0b00000001;    //AN0(GP0)は入力ピン, 他は出力ピン
    CMCON0 = 0b00000111;    //コンパレータを使用しない

    unsigned char data[6][5] = {{0, 0, 0, 0, 0}, {0, 0, 0, 0, 1}, {0, 0, 0, 1, 0}, {0, 0, 1, 0, 0}, {0, 1, 0, 0, 0}, {1, 0, 0, 0, 0}};

    while(1){
        sendLedData(data[(adconv() / 200)], 5);
    }

    return;
}

void sendLedData(const unsigned char *data, signed char len){

    //シフトレジスタ初期化
    SRCLR = 0;
    SRCLR = 1;

    len--;

    while(len >= 0){
        SER = data[len];
        SRCLK = 1;
        SRCLK = 0;
        len--;
    }

    RCLK = 1;
    RCLK = 0;
}

unsigned int adconv(){

    ADCON0 = 0b10000001;    //右寄せ, 基準電圧は電源電圧, AN0をA/Dの Input, 変換は停止中, A/D変換を有効
    __delay_us(20);
    GO = 1;
    while(GO);

    return (ADRESH << 8) + ADRESL;
}

プログラムの解説

今回初めて使う部分はadconv()関数内です。そのほかは前記事(PIC12F683 / PIC16F1937を使ってみる実験的な)に書いてあります。

main関数内のPICの設定部分でアナログ入力がGP0/AN0/CIN+/ICSPDAT/ULPWUな7番ピンになるように設定しています。この後はadconv関数(アド昆布って読んでしまう…)をそのアナログ入力ピンに入力された数値を読み取るように組んでいきます。
アナログ入力の設定を行うレジスタはADCON0レジスタです。

ADCON0レジスタの7bit目はアナログ入力されてる値を右寄せか左寄せにするかどうかです。

PIC12F683(ほぼほぼのPICに言えるかも?)においてアナログ入力された値は10bitで表現されます。しかしPIC12F683のレジスタは8bitまでの値しか保存できません。そこで、2つのレジスタに分けて保存します。一つを8bit分, もう一つを2bit分使うことになります。
アナログ入力された値のうち上位bitをADRESHレジスタに、下位bitをADRESLレジスタに保存します。
ADCON0レジスタで右寄せを設定したとき上位2bitがADRESHレジスタに、下位8bitがADRESLレジスタに保存されます。逆に左寄せを設定したとき上位8bitがADRESHレジスタに、下位2bitがADRESLレジスタに保存されます。

今回は下位ビットから埋まっていく(直感的に理解しやすい)ように右寄せで設定しました。

ADCON0レジスタの6ビット目は基準電圧の設定です。
基準電圧は入力される電圧の最大値です。今回はPICと同じ電源を使って実験していくので電源電圧(Vdd)を指定します。

ADCON0レジスタの5−4ビット目は何も指定しません。0でいいです。

ADCON0レジスタの3-2ビット目はどのピンを読み取るかです。ここでのピンの表現はANx(0 <= x <= 3)です。データシートを見ましょう。

ADCON0レジスタの1ビット目はA/D変換の進行状況の指定です。1を指定すると即座にA/D変換が始まってしまうので0を指定しておきましょう。

ADCON0レジスタの0ビット目はA/D変換の有効 or 無効設定bitです。A/D変換やるので1を指定。

これでA/D変換の準備が整いました。
A/D変換の開始はADCON0の1bit目を1に指定するか GOレジスタを1に指定するかでできます。
GO使う方が好きです。なのでGO = 1;でA/D変換開始です。
しかしADCON0レジスタは値の指定に微量時間かかる模様。delay関数を入れろとあります。その表がデータシートの9.1章にあります。
OSCCONレジスタで指定したPICの駆動クロックとANSELレジスタの6-4bit(ADCSレジスタ)で指定したもので決定します。
今回は1Mhz駆動でADCSレジスタが000なので2.0usの待機時間が必要です。なので__delay_us(2)…でいいはずですが上のソースでは20us待機してますね。完全に表の読み取りミス。まぁ2usより短くないからいいか…。

A/D変換を開始するときGOレジスタを1に指定しました。変換が終わるとGOレジスタが0になるのでwhile(GO);で変換終了を待ちます。

変換が終了すると値がADRESH, ADRESLレジスタに格納されるのでADRESHレジスタを8bit左シフトした値とADRESLレジスタを足し合わせた値をreturnします。

さて、ここでreturnされる値は最低値, 最大値はなんでしょや。
A/D変換の値は10bit使うと上で書いたのでわかるかと思いますが0〜1023です。負の値は取りません。
これは入力された値を1024段階にしています。(これがDigital–––離散的–––である値)
数式としては
ADRES = Vin / (Vref / 1024)
です。
Vinは入力される電圧, Vrefは基準電圧です。

これがadconv関数です。

あとはこの値を5分割して5つのLEDを光らせているだけです。

実験結果

OLYMPUS DIGITAL CAMERA
OLYMPUS DIGITAL CAMERA

図7

OLYMPUS DIGITAL CAMERA

図8

奥にある半固定抵抗をグリグリ回すとLEDの光る場所が左右に移動します。

Part 3 : モータドライバを使ってみる実験

モータドライバとは

モータドライバとはなんでしょや。
モータドライバはモータの制御をするICです。
PICだけでモータの制御を行うのは難しいです。特に正回転, 逆回転の制御やモータの単位時間あたりの回転数の制御がめんどくさく、何よりPICを動かすパワー(電力)が不足します。
しかしモータドライバを用いればPICとは別の電源(モータ専用電源)を用いつつPICからは3本のピンだけで正回転, 逆回転, 回転速度の制御ができます。

回転数制御に関して

モータの正回転, 逆回転の制御に関しては簡単に実装することができます。これはPIC側がデジタルな制御だけで実現することができるからです。
しかし回転数制御に関してはそうもいきません。なぜなら回転数制御は電圧の制御で実現するからです。
PICにおいてこの電圧の制御はPWM制御と呼ばれます。PICにおいてはCCPモジュールのPWM機能とも。
このPWM制御を用いてモータの単位時間あたりの回転数を制御していきます。

PWM制御

PWM制御とは簡単にいうとON/OFFを高速に変化させて電圧を制御することです。
Googleなどで検索をかけると多くのサイトがヒットするので詳しくはそっちをみてください。ここではPIC12F683にどのように命令するかを書きます。

CCP1CONレジスタ

PWM制御の設定を行うレジスタはCCP1CONレジスタです。もちろんCCPなピンが複数あればCCP1CONだったりCCP2CONだったりします。
PIC12F683にはCCPなピンは1個しか無いのでCCP1CONにて設定を行います。これについてはデータシート75ページに書いてあります。

CCP1CONレジスタの7-6bitは未定義です。0を指定しておきましょう。

CCP1CONレジスタの5-4bitはデューティサイクル比の下位2ビットの指定です。ちなみにデューティサイクルは10bitで指定するもので上位8bitはCCPR1Lで指定します。これについては後述します。

CCP1CONレジスタの3-0bitはモードの選択です。今回はPWMモードで使うので”110x”か”111x”です。active-highで使うので”1100″とでもしておきましょう。

PR2レジスタ

PR2レジスタはPWMの周期の設定を行うレジスタです。
と言っても他のレジスタと違って値を代入するだけです。レジスタなので8bitが上限になります。つまり0〜255までです。

PWMの周期は少しの計算が必要になります。
PWM周期の計算式はデータシートの79ページの式11-1に書いてあります。

PWM周期 = [(PR2) + 1] * 4 * (クロックの逆数) * (TMR2のプリスケーラ値)

になります。

具体例を出しましょう。

PR2を200としてクロックを1MHz, プリスケーラは1としましょう。
この時のPWM周期は

PWM周期 = (200 + 1) * 4 * (1 / (1 * (10^6))) * 1

になります。

T2CONレジスタ

T2CONレジスタはTMR2に関して設定するレジスタです。データシートの50ページにこのレジスタの詳細が書いてあります。

7bit目は未定義です。0を指定しておきましょう。

6-3bitはポストスケーラを設定するbitです。
ポストスケーラはTMR2がn回カウントアップされたら割り込みをする…というものです。ちなみによくわかって無いです。なので1:1にしておきます。
わかったら追記しておきます。

2-0bitはプリスケーラを設定するbitです。
プリスケーラはカウントアップシグナルがm回きた時にTMR2をカウントアップさせる…というものです。これもよくわかってないので1にしておきます。
わかったら追記しておきます。

CCPR1Lレジスタ

PWMについて少し調べるとデューティサイクル比がどうとか…と書いてありますね?
そのデューティサイクル比を指定するのがCCPR1LレジスタとCCP1CONレジスタです。
これは10bitで表すので前の実験で書いたADRESH/ADRESLのごとくレジスタ2つ使用して指定します。

デューティサイクル比は1が出力されている時間の割合のことです。
CCPR1Lレジスタ, CCP1CONにこれを設定する時も少し計算が必要になります。この計算式はデータシートの79ページに書いてあります。

Duty Cycle Ratio = (CCPR1L:CCP1CON) / 4 * (PR2 + 1)

この式のうちCCPR1LとCCP1CONに設定すべきは “(CCPR1L:CCP1CON)” なので式を変形して

(CCPR1L:CCP1CON) = Duty Cycle Ratio * 4 * (PR2 + 1)

になります。

またCCP1CONはCCP1CONの5, 4bitをこの順番に並べるという意味で(CCPR1L:CCP1CON<5:4)と書くとCCPR1LとCCP1CONの5, 4bitをこの順番で並べるという意味です。

もしPR2 = 200, デューティサイクル比 10%とすると

CCP = 0.10 * 4 * (200 + 1)

です。

実際にこれを指定するときは下位2ビットはCCP1CONの5, 4bitに指定しないといけないのでそれを注意しましょう。

実験

実験手順

PIC12F683に以下のプログラム, 回路を組んでモータの回転数を体感します。感じられますね?

モータドライバ実験 回路図
/*
 * File:   pwmtest.c
 * Author: BEAN
 *
 * Created on August 26, 2017, 6:42 PM
 */

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown Out Detect (BOR enabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)

//__delay_ms()関数のためにクロック指定
#define _XTAL_FREQ 1000000

#include <xc.h>

unsigned int adconv();

void main(void) {

    // PIC初期化
    OSCCON = 0b01000000;    //1MHz駆動
    ANSEL  = 0b00000001;    //AN0(GP0)はアナログ入力ピン, 他はデジタルI/O, 変換時間は2us
    TRISIO = 0b00000001;    //AN0(GP0)は入力ピン, 他は出力ピン
    CMCON0 = 0b00000111;    //コンパレータを使用しない

    CCP1CON = 0b00001100;   //PWM有効
    PR2 = 200;              //PWM周期は 2.01 * 4 * 10^(-4)
    T2CON = 0b00000100;     //TMR2使用

    GP5 = 1;    //どちらかに回転させる

    while(1){
        CCPR1L = (int)((adconv() / 10.2) * 2.01);
    }

    return;
}

unsigned int adconv(){

    ADCON0 = 0b10000001;    //右寄せ, 基準電圧は電源電圧, AN0をA/DのInput, 変換は停止中, A/D変換を有効
    __delay_us(20);
    GO = 1;
    while(GO);

    return (ADRESH << 8) + ADRESL; }  

実験結果

うん。変わるね。

Part 4 : ライントレーサを作る

以上の3つの実験を踏まえてライントレーサを製作して行こうと思います。 今回の目標はとりあえずライントレースすること。とします。 具体的に言うとライントレーサのラインをトレースするための制御はON/OFF制御にすると言うことです。これは書いてる今(9/26)現在まだ最終的に実装したい制御方法で動かせていないためです。(夏休み終わったからこれ作り続けるかどうか…) 一応これはライントレーサ Ver.1といった感じです。 またPICを実験で使ったPIC12F683からPIC16F1937に変えて使っていきます。 configとかレジスタの名前が微妙に違ってたりしますがそこはデータシート読んでどうぞ(PIC16F1937は日本語データシートが存在します)

目標

  • ライントレースすること
  • ブレッドボードではなくユニバーサル基板上に実装すること
  • ライントレースするためのスタートボタンをつけること
  • 制御方法はON/OFF制御で行うこと
  • ちょっと歪なラインを1周すること(停止する機能はつけない)

実験

回路

基本的には上でやった実験の回路を組み合わせる感じになります。図9が回路図になります。

linetracer_回路図

図9 : ライントレーサ回路図

プログラム


/*
 * File:   Linetracer_main.c
 * Author: BEAN
 *
 * Created on August 29, 2017, 1:12 AM
 */

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

#define _XTAL_FREQ 1000000

#define LEFT_MOTOR_PIN0 RD0
#define LEFT_MOTOR_PIN1 RD1

#define RIGHT_MOTOR_PIN0 RD3
#define RIGHT_MOTOR_PIN1 RD2

#define START RD7

/* 制御設定(変更可能定数)ここから */

#define OFFSET 421

#define STDSPEED 45
#define LOWSPEED -28


/* 制御設定(変更可能定数)ここまで */

#include <xc.h>

void picInit();

void rightMotor(const signed char speed);
void leftMotor(const signed char speed);

unsigned int readLineColor();   //フォトリフレクタから値を読み取る関数

void rotateMotor(const unsigned char direction, const unsigned char side);      //
void changeMotorSpeed(signed char speed, const unsigned char side);

void main(void) {
    
    /* PIC初期設定 */
    picInit();
    
    /* スタートボタンが押されるまで待機 */
    while(START);
    
    /* 押されて500ミリ秒後にライントレース開始 */
    __delay_ms(500);
    
    
    /* 再び押されるまでライントレース */
    while(START){
        
//        leftMotor(STDSPEED);
//        rightMotor(LOWSPEED);
        
        if(readLineColor() > OFFSET){
        /* 黒色を検知した時 */
            leftMotor(STDSPEED);
            rightMotor(LOWSPEED);
        }else{
        /* 白色を検知した時 */
            rightMotor(STDSPEED);
            leftMotor(LOWSPEED);
        }
    }
    
    rightMotor(0);
    leftMotor(0);
    __delay_ms(500);
    
    return;
}

void picInit(){
    
    /* クロック設定 */
    OSCCON = 0b01011010;    //1Mhz駆動
    
    /* ピン設定 */
    APFCON = 0b00000001;    //CCP2は36番ピンに, CCP3は8番ピンに設定
    ANSELA = 0b00000001;    //AN0(2番ピン)はアナログ入力
    TRISA = 0b00000001;     //AN0(2番ピン)が入力ピン
    ANSELB = 0b00000000;    //CCP2をデジタルI/O
    TRISB = 0b00000000;     //CCP2を出力ピンに設定
    ANSELE = 0b00000000;    //CCP3をデジタルI/O
    TRISE = 0b00000000;     //CCP3を出力ピンに設定
    ANSELD = 0b00000000;    //RDnはデジタルI/O
    TRISD = 0b10000000;     //RD7はデジタル入力, 他はデジタル出力
    
    /* モータが勝手に動かないように */
    LEFT_MOTOR_PIN0 = 0;
    LEFT_MOTOR_PIN1 = 0;
    RIGHT_MOTOR_PIN0 = 0;
    RIGHT_MOTOR_PIN1 = 0;
    
    /* PWM設定 */
    CCP2CON = 0b00001100;   //CCP2のPWM有効
    CCP3CON = 0b00001100;   //CCP3のPWM有効
    CCPTMRS0 = 0;           //CCPのタイマは全てTMR2を使用
    T2CON = 0b00000100;     //TMR2に関して 1:1ポストスケーラ, TMR2有効, プリスケーラは1
    PR2 = 200;              //TMR2に関して PWM周期は2.01 * 4 * 1 * 10^(-4)
    
}

void rightMotor(const signed char speed){
    
    rotateMotor(1, 1);
    changeMotorSpeed(speed, 1);
    
}
void leftMotor(const signed char speed){
    
    rotateMotor(1, 0);
    changeMotorSpeed(speed, 0);
    
}

unsigned int readLineColor(){
    
    ADCON0 = 0b00000001;    //AN0をA/Dのピンに, 変換は停止中, A/D変換を有効
    ADCON1 = 0b11010000;    //右詰, クロックはFosc/16,
    
    __delay_us(10);         //設定待機
    
    ADCON0bits.GO = 1;      //A/D変換開始
    while(ADCON0bits.GO);   //終了まで待つ
    
    return (ADRESH << 8) + ADRESL;
}

void rotateMotor(const unsigned char direction, const unsigned char side){
    
    if(side){
    /* 右側モータの場合 */       
        if(direction){
        /* 前進するようにモータを回転させる */
            RIGHT_MOTOR_PIN0 = 1;
            RIGHT_MOTOR_PIN1 = 0;
        }else{  
        /* 後進させるようにモータを回転させる */
            RIGHT_MOTOR_PIN0 = 0;
            RIGHT_MOTOR_PIN1 = 1;  
        }
    }else{
    /* 左側モータの場合 */    
        if(direction){
        /* 前進するようにモータを回転させる */
            LEFT_MOTOR_PIN0 = 1;
            LEFT_MOTOR_PIN1 = 0;
        }else{   
        /* 後進させるようにモータを回転させる */
            LEFT_MOTOR_PIN0 = 0;
            LEFT_MOTOR_PIN1 = 1;   
        }
    }
    
}

void changeMotorSpeed(signed char speed, const unsigned char side){
    
    /*
     *  speedは-100から100までの間の値をとる。
     * 100以上なら100に減らす。
     * 0以下ならrotateMotor関数で逆回転をさせspeedを正負反転して適応
     */
    
    if(speed > 100) speed = 100;    //speedが100以上の時
    
    if(speed < 0){
    /* speedが負の値の時 */
        rotateMotor(0, side);
        speed = -speed;
    }
    
    if(side){
    /* 右側モータの場合 */
        CCPR2L = (int)((speed * 2.01) + 0.5);
    }else{
    /* 左側モータの場合 */
        CCPR3L = (int)((speed * 2.01) + 0.5);
    }
    
}

実験結果

まぁ一応動いた…て感じで。

今後…

今後の目標としてハード, ソフト, 機能面で盛りだくさんです。

ハード面の目標

  • 後輪をまともなものにしたい
  • 光らせたい
  • コンパクトにしたい

ソフト面の目標

  • PID制御にしたい
  • モータドライバ周りの制御の改善

機能面の目標

  • ステータスランプ的なのつけたい
  • リモコンで操りたい
  • PICへの書き込み回路も載せたい

これ…出来んのか…?

早急に取り掛かりたいのは後輪, PID, モータドライバの改善ですかね。
特に後輪はアレすぎる。

後輪

図10 : 後輪

これ余り物を適当に組み合わせた後輪的な何かです。
もともともうちょっとマシそうな

元の後輪

図11 : 元の後輪

こんなのつけていたのですが、汚れとか巻き込んじゃって回らなくなってしまいました。
それで、適当に図10を作り上げてつけたわけです。

タミヤで一輪ってのがないんですよね。後輪の高さとか考えないとダメですし…ぴったりなものがなかなか見つからないんですよね。

あとがき

iPhone 8 欲しい!

これが最近思うことです。
今はHUAWEI P10 lite (HUAWEIがいいのかHuaweiがいいのか)をメインに使っていますが早くもiPhoneが欲しくなってきました。
この物欲が新しいもの欲しさからなのかMacと相性の悪いAndroidによるストレスを減らしたい思いからなのか。
とにかく欲しいですね。
背面ガラスということでiPhone 4, 4sのような黒色が読みがるのかと思ったわけですが実際みた背面の色は黒ではないですね。そりゃそうですスペースグレイなんですから。個人的にはガラスかつ4, 4sのような”黒”が欲しかったわけですがそれはないようで…。とはいえ買うならスペースグレイですね。白だと液晶部分とそうでない部分が目立っちゃう(それが好きではない)ので。

Ubuntu on Nexus7 !

これが最近やってみたことです。
Ubuntu 13.04 をNexus7に焼いてみました。今更感ありますがこれ楽しいです。あと思ってたより動作が軽い。さすがにブラウザとかは重いですがそんなんは求めていないです。シェルが開ければ十分で。
しかしやることもなかったので結局Androidに戻しました。ちなみにLineage OS 11というUnofficialなROM焼いて使ってます。(Lineage OS 11なんて公式で ないよ)

DAIGAKU NO HAZIMARI

また遠くの大学まで行かなければなりません。電車が非常に辛い。バスも辛い。歩け。

今期目標

LPIC Level 1取りたいですね。
あと運転免許。
TOEICも受けなくてはならないし、これはちゃんと勉強したいです。

こんなに欲しいものがあると実装する時間がないんですよね。ライントレーサの完成はいつになるやら…。

参考にしたサイト

広告

PIC12F683 / PIC16F1937を使ってみる実験的な

 <HEADER>

あ、いつものgdgd無駄文章はカットです。

今回やりたいことまとめ

  • PIC12F683で簡単なプログラム動かしてみる : 所謂Lチカ的な。
  • 8ビットシフトレジスタを使ってみる(なおLEDの個数が足りない模様) : 複数のLEDを少ないピンで制御する。
  • 赤外線で通信してみる : PIC16F1937とRaspberry Pi 3と簡単な通信をしてみる。

この3点やってみようと思います。

“PIC12F683 / PIC16F1937を使ってみる実験的な” の続きを読む

Ingress エージェント的にHuawei P10 lite。

“Ingress エージェント的にHuawei P10 lite。” の続きを読む

h.ear go (SRS-HG1)を約1ヶ月使ってみて。

“h.ear go (SRS-HG1)を約1ヶ月使ってみて。” の続きを読む

Raspberry Pi 3 でLCD(ACM1602NI-FLW-FBW)

“Raspberry Pi 3 でLCD(ACM1602NI-FLW-FBW)” の続きを読む

Raspberry Pi 3 をセットアップ+α

“Raspberry Pi 3 をセットアップ+α” の続きを読む

Nucleo F401-REで角度測ったり。

“Nucleo F401-REで角度測ったり。” の続きを読む