Vol.895 31.May.2024

コンプレッサーの選び方 PICミニBB(5)_SPI接続グラフィックLCDユニット

C コンプレッサーの選び方

by fjk

 タイヤ交換時にコンプレッサーを利用しているが、購入からかなり時間がたっており、少しエアーが漏れているようなのでそろそろ交換時期かなと思い、購入時に確認すべき仕様について纏めてみた。

  1. ポンプ方式・騒音
    オイルフリー ○:クリーンエアー、オイル交換不要
    ×:耐久性・連続使用時間に制限
    給油式 ○:連続使用、低騒音・低振動
    ×:オイルミスト、オイル交換
  2. 吐出空気圧(出力)と用途
      3馬力以上は家庭用コンセントの電気容量を超えるので、2馬力までが選択子。
       
  3. タンク容量
    容 量主な用途
    〜8Lエアブラシ(細かな塗装)
    8L〜タイヤの空気入れ、短時間の埃飛ばし、エアタッカー、釘打ち機 
    30L〜タイヤ交換、エアダスター、スプレーガン、サンドブラスト
    50L〜連続使用エア工具
  4. タンク内圧力
     0.8〜4.5Mpaのものがあるが、家庭用では0.8または1.0Mpaがほとんど。

 すなわち、家庭用では1馬力、タンク容量が30Lを選択しておくと無難。
 騒音を気にするなら、給油(オイル)式の静音タイプが選択子。


P PICミニBBシリーズ(5)  〜SPI接続グラフィックLCDユニット

by fjk

 秋月電子で売られているSPI接続の「超小型グラフィックLCD」(AQM1248A、ピッチ変換キット、秋月#107007、800円)にPICで文字を表示してみた。なお、AQM1248Aは3.3Vで動作するため、PICを5Vで使う場合、SPI信号レベルの変換を行う必要が有り、このレベル変換用として「4ビット双方向ロジックレベル変換モジュール」(AE-LCNV4、秋月#113837、200円)と3.3Vの3端子レギュレータ(LM2950G-3.3、秋月#116990、160円)を用いた。
 マスターとなるPIC-BASIC-spiはPIC16F18325(秋月#110890、220円)を用い、gLCDユニットとは10ピンのコネクタを使用した(abc894等と配線が少し異なる)。なお、グラフィックBB側の未使用のコネクタピンは、BB上で信号がぶつからないよう、ラジオペンチで抜いている


SPI接続グラフィックLCD回路図

グラフィックLCDボード配線図

MCCのpinmodule設定

MCCのsystem設定

MCCのSPI設定

 今回、プログラムを簡単にするため、文字表示位置のX(水平)方向は任意の位置に指定可能だが、Y(垂直)方向は8ドット(LCDのバイト)単位の位置とした(Y座標の8ドット未満は無視)。
 文字フォントはグラフィック表示できるので「はじめてのPIC2」さんの5x7ドットプロポーショナルフォントを利用させていただいた。
 さて、MMCでGenerateし、プログラムを書き込んで、実行(run)してみるが・・、何故かSPIが動作していない。オシロで確認すると、クロックが出ていない!!。
 そこで、ネットで調べて見ると、「ENGピカ」さんのページに、その問題と解決策が紹介されていた。その内容は、

MCCで生成されたSPI1_Initialize()関数が初期化時にコールされますが、SPIが許可されないため、別途レジスタを操作してSPIをスタートする必要があります。そこで、system初期化後に以下を記述を追加:
    SSP1CON1bits.SSPEN = 1;

 この問題は、MCCのMSSP1で、RegstersのSSP1CON1のSSPENをenableに変更し、Generateしても、SPI1_Initialize()関数の最後で「SSP1CON1bits.SSPEN = 0;」とされているので、MCC設定とうりには反映されない(MCCのバグ?、ここで変更しても良い)。

 ということで、上記の記述を追加すると、無事SPIが動作し、AQM1248AグラフィックLCDに文字などを表示することができた。
 今回、SPIの通信クロックとして、I2C(100kHz)よりも早い2MHzを使用したが、正常に通信することができた。
 なお、5V電源としても使用しているUSBによるパソコンとのターミナル通信はデバッグ(動作確認)用で、動作モニターとして使用しないのであればEUSARTの設定(およびputs)は不要。(EUSARTの設定はabc889を参照)


AQM1248Aにデモ表示中

AQM1248A画面の拡大

【参考とした・・】
*1)「逆引きPIC電子工作やりたいこと事典」(後閑哲也、技術評論社)
*2)「はじめてのPIC-2」さん
*3)「畑から」さん

【プログラム】 abc895-18325.c(zip)
※ライブラリにバグがあったのと、全関数をテストするためのプログラムに修正しました。(6/2)

/*******************************(abc895-18325)******
 *   Graphic LCD (AQM1248A) test  
 ***************************************************/

#include "mcc_generated_files/mcc.h"
#include "AQM1248A.h"

char     sBf[BFSIZE];       // 文字列作業エリア
char     rBuf[BFSIZE];      // シリアル受信バッファ
uint8_t  sFlg;              // シリアル受信フラグ

char gptn[] = { 0x01,0x03,0x07,0x0F,
                0x1F,0x3F,0x7F,0xFF,
                0xFE,0xFC,0xF8,0xF0,
                0xE0,0xC0,0x80,0x00 };

/*-----------------------------------
 *        Main application
 ------------------------------------*/
void main(void){
    char ch;
    uint8_t xpos;

    SYSTEM_Initialize();
    SSP1CON1bits.SSPEN = 1;     //SPI1を有効に

    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    gLCD_int();
    gLCD_clr(0);

    puts("-- Hellow AQM1248A ! --\n")  // 動作確認用

    while (1){
        gLCD_posyx(0,0);
        gLCD_str("-- Hellow AQM1248A ! --");
        gLCD_chr(0x22);
        gLCD_posyx(8,0);
        for(ch = 0x23; ch < 0x3A; ch++){
            gLCD_chr(ch);
        }
        gLCD_posyx(16,0);
        for(ch = 0x40; ch < 0x50; ch++){
            gLCD_chr(ch);
        }
        gLCD_posyx(24,0);
        gLCD_str("PQRSTUVWXYZ");
        gLCD_clrX(0xCA,24,100,20);     
        xpos = 0;
        for(ch = 0x60; ch < 0x70; ch++){
            xpos = gLCD_chrX(ch,32,xpos);
        }
        gLCD_strX("pqrstuvwxyz",40,0);
        xpos = gLCD_ptnX(gptn,40,100,16);
        xpos = gLCD_ptnX(gptn,40,xpos,16);   
    }
}

【AQM1248Aライブラリ】
 SPI通信でAQM1248Aにコマンドやデータを送信するには、SPI1_ExchangeByte()関数を用いた。
 送信手順は、AQM1248AのCSをLowにし、続いてRSをLowにしてコマンド送り、それからRSをHighに指定しデータを送る。送信終了後は、他のデバイスとぶつからないよう、CSをHighに戻しておく。
 データのブロック通信用としてSPI1_ExchangBlock()関数があるが、blockにパターンデータを指定したところ、何故か全て0xFFデータとして表示されたので、SPI1_ExchangeByte()関数を繰り返し使用した(今後の課題)。なお、gLCD_clrX()関数は任意のエリアを消去(塗りつぶす)ことができる。

AQM1248A.c/h(zip)

/***********************************(AQM1248A.c)********
 *     RC3  gRS     0:コマンド  1:データ
 *     RA2  gCS     0:有効      1:無効
 *     RC2  spiSDO
 *     RC0  spiSCK     idle時:Hi   SCKの立下でデータ変更
 **********************************************************/

#include "mcc_generated_files/mcc.h"
#include "AQM1248A.h"

//-------- G-LCDを初期化 ------------------------------------
void gLCD_int (void){
    gCS_SetLow();                   // チップセレクト有効
    gRS_SetLow();                   // コマンドデータ指定
    SPI1_ExchangeByte(0xAE);        // Display = OFF
    SPI1_ExchangeByte(0xA0);        // ADC = normal
    SPI1_ExchangeByte(0xC8);        // C.output = revers
    SPI1_ExchangeByte(0xA3);        // bias = 1/7
                               //--内部レギュレータをON
    SPI1_ExchangeByte(0x2C);        // power control 1
    __delay_ms(2);                  // 2mS遅延
    SPI1_ExchangeByte(0x2E);        // power control 2
    __delay_ms(2);                  // 2mS遅延
    SPI1_ExchangeByte(0x2F);        // power control 3
                               //--コントラスト設定
    SPI1_ExchangeByte(0x23);        // Vo ratio set
    SPI1_ExchangeByte(0x81);        // E-volume mode
    SPI1_ExchangeByte(0x1C);        // E-volume value
                               //--表示設定
    SPI1_ExchangeByte(0xA4);        // 全点灯しない
    SPI1_ExchangeByte(0x40);        // start line = 0
    SPI1_ExchangeByte(0xA6);        // 白黒反転しない
    SPI1_ExchangeByte(0xAF);        // Display = ON
    gCS_SetHigh();                  // チップセレクト無効
}

//-------- G-LCDに一文字表示する ------------------------
void gLCD_chr (char dat){
    char i,ptn;
    dat -= 0x20;                    //配列アドレスを計算
    gCS_SetLow();
    gRS_SetHigh();
    for(i=0; i<5; i++) {
        ptn = Font[dat][i];
        if(ptn == 0xFF) break;      //幅狭文字なら抜ける
        SPI1_ExchangeByte(ptn);
    }
    SPI1_ExchangeByte(0);
    gCS_SetHigh();
}

//-------- G-LCDのデータを全消去(指定データで埋める)---
void gLCD_clr (char dat){			
    uint8_t x,y;
    gCS_SetLow();                   // チップセレクト有効
    for(y = 0; y < 6; y++){
        gRS_SetLow();               // コマンド指定
        SPI1_ExchangeByte(0xB0 + y);
        SPI1_ExchangeByte(0x10);
        SPI1_ExchangeByte(0x00);
        gRS_SetHigh();              // 表示データ指定
        for(x = 0; x < 128; x++){
            SPI1_ExchangeByte(dat);
        }			
    }
    gCS_SetHigh();                  // チップセレクト無効
}

//-------- カーソル位置指定 -----------------------------------
void gLCD_posyx(uint8_t ypos, uint8_t xpos){
    gCS_SetLow();                   // チップセレクト有効
    gRS_SetLow();                   // コマンド指定
                                // ページを指定
    SPI1_ExchangeByte(0xB0 | (ypos >> 3));
                                    // ページ内RAMアドレスを分割指定
    SPI1_ExchangeByte(0x10 | (xpos >> 4));
    SPI1_ExchangeByte(xpos & 0xF);
    gCS_SetHigh();                  // チップセレクト無効
}

//-------- 文字列出力 ------------------------------------------
void gLCD_str(char *str){			
    while(*str)                     //文字列終端まで継続
        gLCD_chr(*str++);           //文字出力
}

//-------- 指定位置に文字表示(次xpos位置を返す) ---------
uint8_t gLCD_chrX(char dat, uint8_t ypos, uint8_t xpos){
    uint8_t i,ptn;					
    dat -= 0x20;                    //配列アドレスを計算
    gLCD_posyx( ypos, xpos);        //表示位置を指定
    gCS_SetLow();                   // チップセレクト有効
    gRS_SetHigh();                  // 表示データ指定
    for(i = 0; i < 5; i++) {        //データを順に取得
        ptn = Font[dat][i];
        if(ptn == 0xFF)  break;     //幅狭文字なら抜ける
        SPI1_ExchangeByte(ptn);
    }                 
    SPI1_ExchangeByte(0);           //文字間隔を空ける
    gCS_SetHigh();                  // チップセレクト無効
    return i + 1 + xpos;            //次表示位置を返す
}

//-------- 指定位置から文字列表示(次xpos位置を返す)-----
uint8_t gLCD_strX(char *str, uint8_t ypos, uint8_t xpos){
    while(*str)                     //文字列終端まで継続
        xpos = gLCD_chrX(*str++, ypos, xpos);
    return xpos;                    //次表示位置を返す
}

//-------- 指定位置からパターンを表示(次xpos位置を返す)----
uint8_t gLCD_ptnX(char *ptn, uint8_t ypos, uint8_t xpos, uint8_t n){
    uint8_t i;
    gLCD_posyx(ypos, xpos);         // 表示位置を指定
    gCS_SetLow();                   // チップセレクト有効
    gRS_SetHigh();                  // 表示データ指定
    for(i=0; i < n;i++){
         SPI1_ExchangeByte(ptn[i]);
    }
//    SPI1_ExchangeBlock(ptn, n);   // ブロックデータ送信
    gCS_SetHigh();                  // チップセレクト無効
    return xpos + n;                // 次表示位置を返す
}

//------- 指定位置からnバイトを指定データで埋める(次xpos位置返す)----
uint8_t gLCD_clrX(char dat, uint8_t ypos, uint8_t xpos,uint8_t n){
    uint8_t i;
    gLCD_posyx(ypos,xpos);          // 表示位置を指定
    gCS_SetLow();                   // チップセレクト有効
    gRS_SetHigh();                  // 表示データ指定
    for(i = 0; i < n; i++) {
        SPI1_ExchangeByte(dat);
    }
    gCS_SetHigh();                  // チップセレクト無効
    return xpos + n;                // 次開始位置を返す
}

/******************************(AQN1248A.h)***************
 *   Grafic LCD  AQM1248A.h 用ライブラリ
 *   (5x7dotのプロポーショナル文字パターン付き)
 **********************************************************/
/*  G_LCDを初期化する
 *	gLCD_int(void); */
void gLCD_int(void);

/*  G_LCDのRAMの書込みアドレスを指定する
 *	gLCD_posyx(ypos,xpos);
 *	  表示文字位置(ypos,xpos) */
void gLCD_posyx(uint8_t, uint8_t );

/*  G_LCDにchコードの文字パターンを表示
 *	gLCD_chr(ch);
 *	  表示文字コード(ch) */
void gLCD_chr(char );

/*  文字列strをG_LCDに表示する
 *	gLCD_str(*str);
 *	  表示文字列(*str) */
void gLCD_str(char *);

/*  G_LCDのRAMを全消去(指定データで埋める)
 *	gLCD_clr(dat);
 *	  埋めたい1バイトデータ(dat) */
void gLCD_clr(char );

/*==== 次xpos位置を返す関数 ======*/
/*  G_LCDにchrコードの文字パターンを表示
 *	gLCD_chrX(ch, ypos, xpos);
 *	  表示文字(ch)、表示位置(ypos,xpos)  */
uint8_t gLCD_chrX(char , uint8_t , uint8_t );

/*  文字列strをG_LCDに表示する
 *	gLCD_strX(*str, ypos, xpos);
 *	  表示文字列(str)、表示位置(ypos,xpos) */
uint8_t gLCD_strX(char *, uint8_t , uint8_t );

/*  ビットパターンをG_LCDに表示する
 *	gLCD_ptnX(*ptn, ypos, xpos);
 *	  パターン(ptn)、表示位置(ypos, xpos)、転送数(n)  */
uint8_t gLCD_ptnX(char *, uint8_t , uint8_t , uint8_t );

/*  指定したdatで指定数nだけ表示
 *	gLCD_clrX(dat, ypos, xpos, n);
 *	  データ(dat)、表示位置(ypos, xpos)、転送数(n) */
uint8_t gLCD_clrX(char , uint8_t , uint8_t , uint8_t );

/*********************************************************
 * キャラクタデータ (ASCII 0x20-0x7F) 
 *      5dotない幅の狭いフォント(!など)は0xFFで終端
**********************************************************/
const char Font[96][5]={
        {0x00,0x00,0xFF,0x00,0x00},     // 0x20, Space
        {0x5F,0xFF               },     // 0x21, ! x
        {0x00,0x07,0x00,0x07,0x00},     // 0x22, " x
        {0x14,0x7F,0x14,0x7F,0x14},     // 0x23, #
        {0x24,0x2A,0x7F,0x2A,0x12},     // 0x24, $
        {0x27,0x15,0x6B,0x54,0x72},     // 0x25, %
        {0x36,0x49,0x56,0x20,0x50},     // 0x26, &
        {0x0B,0x07,0x00,0xFF     },     // 0x27, ' x
        {0x1C,0x22,0x41,0xFF     },     // 0x28, ( x
        {0x41,0x22,0x1C,0xFF     },     // 0x29, ) x
        {0x2A,0x1C,0x7F,0x1C,0x2A},     // 0x2a, *
        {0x08,0x08,0x3E,0x08,0x08},     // 0x2b, + x
        {0x58,0x38,0xFF          },     // 0x2c, , x
        {0x08,0x08,0x08,0x08,0x08},     // 0x2d, -
        {0x60,0x60,0xFF          },     // 0x2e, . x
        {0x20,0x10,0x08,0x04,0x02},     // 0x2f, /
        {0x3E,0x51,0x49,0x45,0x3E},     // 0x30, 0
        {0x42,0x7F,0x40,0xFF     },     // 0x31, 1 x
        {0x72,0x49,0x49,0x49,0x46},     // 0x32, 2
        {0x22,0x41,0x49,0x49,0x36},     // 0x33, 3
        {0x18,0x14,0x12,0x7F,0x10},     // 0x34, 4
        {0x27,0x45,0x45,0x45,0x39},     // 0x35, 5
        {0x3C,0x4A,0x49,0x49,0x30},     // 0x36, 6
        {0x01,0x71,0x09,0x05,0x03},     // 0x37, 7
        {0x36,0x49,0x49,0x49,0x36},     // 0x38, 8
        {0x06,0x49,0x49,0x29,0x1E},     // 0x39, 9
        {0x36,0x36,0xFF,0x00     },     // 0x3a, : x
        {0x5B,0x3B,0xFF,0x00     },     // 0x3b, ; x
        {0x08,0x14,0x22,0x41,0xFF},     // 0x3c, < x
        {0x14,0x14,0x14,0x14,0x14},     // 0x3d, =
        {0x41,0x22,0x14,0x08,0xFF},     // 0x3e, > x
        {0x02,0x01,0x51,0x09,0x06},     // 0x3f, ?
        {0x32,0x49,0x79,0x41,0x3E},     // 0x40, @
        {0x7C,0x12,0x11,0x12,0x7C},     // 0x41, A 
        {0x41,0x7F,0x49,0x49,0x36},     // 0x42, B 
        {0x3E,0x41,0x41,0x41,0x22},     // 0x43, C 
        {0x41,0x7F,0x41,0x41,0x3E},     // 0x44, D 
        {0x7F,0x49,0x49,0x41,0x41},     // 0x45, E 
        {0x7F,0x09,0x09,0x01,0x01},     // 0x46, F 
        {0x3E,0x41,0x49,0x49,0x3A},     // 0x37, G 
        {0x7F,0x08,0x08,0x08,0x7F},     // 0x48, H 
        {0x41,0x7F,0x41,0xFF     },     // 0x49, I
        {0x20,0x40,0x41,0x3F,0x01},     // 0x4a, J 
        {0x7F,0x08,0x14,0x22,0x41},     // 0x4b, K 
        {0x7F,0x40,0x40,0x40,0x40},     // 0x4c, L 
        {0x7F,0x02,0x0C,0x02,0x7F},     // 0x4d, M 
        {0x7F,0x02,0x04,0x08,0x7F},     // 0x4e, N 
        {0x3E,0x41,0x41,0x41,0x3E},     // 0x4f, O 
        {0x7F,0x09,0x09,0x09,0x06},     // 0x50, P 
        {0x3E,0x41,0x51,0x21,0x5E},     // 0x51, Q
        {0x7F,0x09,0x19,0x29,0x46},     // 0x52, R 
        {0x26,0x49,0x49,0x49,0x32},     // 0x53, S 
        {0x01,0x01,0x7F,0x01,0x01},     // 0x54, T 
        {0x3F,0x40,0x40,0x40,0x3F},     // 0x55, U 
        {0x07,0x18,0x60,0x18,0x07},     // 0x56, V 
        {0x7F,0x20,0x18,0x20,0x7F},     // 0x57, W 
        {0x63,0x14,0x08,0x14,0x63},     // 0x58, X 
        {0x03,0x04,0x78,0x04,0x03},     // 0x59, Y 
        {0x61,0x51,0x49,0x45,0x43},     // 0x5a, Z 
        {0x00,0x7F,0x41,0x41,0xFF},     // 0x5b, [ x
        {0x02,0x04,0x08,0x10,0x20},     // 0x5c,  
        {0x41,0x41,0x7F,0x00,0xFF},     // 0x5d, ] x
        {0x04,0x02,0x01,0x02,0x04},     // 0x5e, ^ 
        {0x40,0x40,0x40,0x40,0x40},      // 0x5f, _
        {0x00,0x03,0x04,0xFF,0xFF},     // 0x60, ` x
        {0x20,0x54,0x54,0x54,0x38},     // 0x61, a
        {0x7F,0x28,0x44,0x44,0x38},     // 0x62, b
        {0x38,0x44,0x44,0x44,0xFF},     // 0x63, c x
        {0x38,0x44,0x44,0x28,0x7F},     // 0x64, d
        {0x38,0x54,0x54,0x54,0x18},     // 0x65, e
        {0x08,0x3F,0x09,0x09,0x02},     // 0x66, f
//        {0x08,0x54,0x54,0x54,0x3C},   // 0x67, g
        {0x18,0xA4,0xA4,0x9C,0x78},     // 0x67, g
        {0x7F,0x08,0x04,0x04,0x78},     // 0x68, h
        {0x44,0x7D,0x40,0xFF     },     // 0x69, i x
        {0x20,0x40,0x44,0x3D,0xFF},     // 0x6a, j x
        {0x7F,0x10,0x28,0x44,0xFF},     // 0x6b, k x
        {0x41,0x7F,0x40,0xFF     },     // 0x6c, l x
        {0x7C,0x04,0x78,0x04,0x78},     // 0x6d, m
        {0x7C,0x08,0x04,0x04,0x78},     // 0x6e, n
        {0x38,0x44,0x44,0x44,0x38},     // 0x6f, o
//        {0x7C,0x14,0x14,0x14,0x08},   // 0x70, p
        {0xFC,0x18,0x24,0x24,0x18},     // 0x70, p
//        {0x08,0x14,0x14,0x14,0x7C},   // 0x71, q
        {0x18,0x24,0x24,0x18,0xFC},     // 0x71, q
        {0x7C,0x08,0x04,0x04,0x08},     // 0x72, r
        {0x48,0x54,0x54,0x54,0x24},     // 0x73, s
        {0x04,0x3F,0x44,0x44,0xFF},     // 0x74, t x
        {0x3C,0x40,0x40,0x20,0x7C},     // 0x75, u
        {0x1C,0x20,0x40,0x20,0x1C},     // 0x76, v
        {0x3C,0x40,0x30,0x40,0x3C},     // 0x77, w
        {0x44,0x28,0x10,0x28,0x44},     // 0x78, x
        {0x0C,0x90,0x90,0x90,0x7C},     // 0x79, y
        {0x24,0x64,0x54,0x4C,0x24},     // 0x7a, z
        {0x08,0x36,0x41     ,0xFF},     // 0x7b, x
        {0x00,0x7F,0x00     ,0xFF},     // 0x7c, | x
        {0x41,0x36,0x08     ,0xFF},     // 0x7d, } x
        {0x02,0x01,0x02,0x04,0x02},     // 0x7e, ~ x
        {0x7F,0x7F,0x7F,0x7F,0x7F},     // 0x7f, DELx
    };


※プログラムのリストをハイライト付きのスタイルで見る場合はここをクリック


※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。


コンプレッサーの選び方 PICミニBB(5)_SPI接続グラフィックLCDユニット