目次

BananaPI-M1を使って、オーディオプレイヤーを製作する

製作のきっかけ

今となってはスペック不足となって、使わなくなったBananaPI M1。
せっかくなので、就寝時のオーディオプレイヤーとして、色々と機能を持たせて製作したい。

そもそも著者は、就寝時に音楽をかけながら眠りにつくので、少し特殊な使い方(要件)となるので、
備忘として残しておく

製作にあたり

今回は色々と弄ったり、PICを使って利便性を上げたりなどしていて、資料が多くなる事が予想されるので、
章立てで進めて行くことにする。

  1. BananaPI M1 (BPI) をオーディオプレイヤーに組み込む
  2. BPI(オーディオプレイヤーとして)の利便性UPと節電対策
  3. PC接続のスピーカーをBPIと共用して、1台のスピーカーで利用

1. BananaPI M1 (BPI) をオーディオプレイヤーにする

基本要件
構想/テスト

とりあえず、音が出る事を確認。(mp3 or wav ファイル)

2. BPI(オーディオプレイヤーとして)の利便性UPと節電対策

基本要件
拡張要件(可能であれば)

ヘッドレス状態で

構想/テスト

3. PC接続のスピーカーをBPIと共用して、1台のスピーカーで利用

PC用スピーカーとBPI用スピーカーの2台を置く事は、置き場所の問題もさることながら、
アンプ付きスピーカーである為、USB電源ではあるけど、配線周りもごちゃごちゃする。
また、節電対策としても(チューニングすれば可能かもしれないが)、PCやBPIのシャットダウン状態では、
USB電源は供給されたままであり、スピーカー電源が入りっぱなしだ。

PICやリレーを使って、スピーカー1台を共用する事にする。
今回の要件では、PCとBPIを同時に音を鳴らす必要は無い。

3.1 スピーカーへの電源供給コントロールと音声自動切替

基本要件

・ スピーカーの電源は、

・ スピーカーへの音声出力は、

構想/テスト

PCのシャットダウン状態のUSB電源供給OFFは、BIOS設定で可能となったが、
BPI側の設定が不明 (そもそも設定可能かも不明)
BPI側のUSB電源供給ストップについては、後述する「3.2 BananaPIの電源供給コントロールの仕組み」で実装する。

ブレッドボード上のテストでは、BPI側の電源供給はストップ可能であると仮定して、単体テストを行う。

回路図

プログラム
/*
 * File:   main.c
 * Author: kampari
 * PIC:    12F509
 * MPLAB X IDE V6.20
 * XC8 V2.46
 * Created on 2024/09/15, 19:57
 *
 * PCとBananaPiをスピーカー1台で利用する オーディオセレクタ
 * 
 * 接続環境/仕様
 * PC、BPIからそれぞれUSB(5V受電用)と音声出力(3.5φ)
 * スピーカーは、電源供給用USB(5V) と音声入力(3.5φ)
 * スピーカーの電源供給は、PC、BPIから供給し、PC、BPIの電源ONにて供給
 * PC、BPIが2台同時ON状態時は、BPIからの音声を優先とし、
 * スライドSWでPC優先に変更可能とする
 * PC、BPI両方の電源OFF時は、スピーカーの電源供給OFFとする
 * BPIはshutdownで電源OFFとなるが、未来RPI置き換え時(shutdownでhalt)を考慮しておく
 * 
 * 配線仕様
 *                 VDD   VSS
 *    PC 5V(IN)    GP5   GP0  スピーカー電源供給flag   (ON=1 OFF=0)
 *   BPI 5V(IN)    GP4   GP1  音声セレクタ(リレー電源) (BPI ON時 1 OFF時0)  
 *            GP3(MCR)   GP2  PC優先flag (優先時=1 Default=0)
 */

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// PIC12F509
#pragma config OSC   = IntRC    // Oscillator Selection bits (internal RC oscillator)
#pragma config WDT   = OFF      // Watchdog Timer Enable bit (WDT disabled)
#pragma config CP    = OFF      // Code Protection bit (Code protection off)
#pragma config MCLRE = ON       // GP3/MCLR Pin Function Select bit (GP3/MCLR pinfunction is digital input, MCLR internally tied to VDD)

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

#define ON  1
#define OFF 0

void main(void)
{
    // 12F509 Settiing
    OSCCAL   = 0b01111110;      // 4MHz
    TRISGPIO = 0b00111100;      // GPIO Setting GP2-5:InPut GP0-1:OutPut

    CLRWDT();
    OPTION = 0b11000111;        // BIT0-2:TMR0 Prescaler 256

    GP0 = OFF;
    GP1 = OFF;
    register unsigned char i = 0;
    register unsigned char j = 0;
    register unsigned char k = 0;
    
    while(1){
        for( i = 0 ; i < 6; i++ ) {  
            if ( ( GP4 == ON ) || ( GP5 == ON ) ){
                j++;
            }
            if ( ( GP4 == OFF ) && ( GP5 == OFF ) ){
                k++;
            }
            __delay_ms(500);
            
        }
        if ( j == 6 ){
            if ( GP0 == OFF ) { 
                GP0 = ON;
            }
            if ( ( GP4 == ON ) && ( GP2 == OFF ) && ( GP1 == OFF ) ){
                GP1 = ON;
            }
            else if ( ( GP4 == ON ) && ( GP2 == ON ) && ( GP1 == ON ) ){
                GP1 = OFF;
            }
            else if ( ( GP4 == OFF ) && ( GP1 == ON ) ){
                GP1 = OFF;
            }
        }
        if ( ( k == 6) && ( GP0 == ON) ) {
            GP0 = OFF;
            GP1 = OFF;
        }
        j = 0;
        k = 0;
    }
}

3.2 BananaPIの電源供給コントロールの仕組み

BPI shutdown時にUSB供給をカットする為には、BPIへの電源供給をカットする為に、PICとリレーを使い、電源コントロールを行う。

基本要件
構想/テスト

BPIのUSB電源供給及び、GPIOピン(5V,3.3V,信号ピンなど)は、以下の3パターンである事が分かった。

A. BPI電源未接続状態
  → USB電源供給及び、GPIOピンはLow (当たり前)

B. BPI電源ON状態
  → USB電源供給は5V。 GPIOピン(5V,3.3V)は出力。他の信号PinのHigh/Lowは設定による。

C. BPI電源ON後にshutdown実施 (電源接続状態)
  → LEDランプが消灯するので、USBやGPIOピン電圧も供給停止となると思いきや、
    上記Bの状態を維持。
    著者はケースファンは未装着なのだが、装着しているとshutdown後も回りっぱなしとなるだろう。

上記の状態からGPIOピンの状態を調査。BPI電源ON状態でデフォルトLow状態。OSからコマンドでHighへ変更可能であること。
もしくは、OS起動状態でHigh状態。上記Cの状態でLow状態である事。
この条件に一致するのは、GPIO J12 Pin5番であり、Pin5のLow/Highにて判定する。

回路図

プログラム
/*
 * File:   main.c
 * Author: kampari
 * PIC:    12F509
 * MPLAB X IDE V6.20
 * XC8 V2.46
 * Created on 2024/09/15, 19:57
 * 
 * BananaPiの電源を外部リレーを使ってOFF/ONする
 * 
 * 接続環境/仕様
 * Switch押下されるまで待ち状態(何もしない)
 * GP5 Switch ONにすると、GP0はHighを出力 (リレー動作 BPIへ電源供給開始)
 * GP4 がHigh入力後に、一定時間 Low状態となった場合、GP0はLowを出力 (リレー復帰 BPIへ電源供給停止)
 * GP3 Switch ONにすると、MCLR (リレー復帰 BPIへ電源供給停止)
 * 
 * 配線仕様
 *                   VDD   VSS
 *     ON Switch     GP5   GP0  BPI電源供給(リレー電源) (BPI ON時 1 OFF時0)
 *  BPI halt状態監視  GP4   GP1 動作確認用LED     
 *   Reset Switch    GP3   GP2  
 */

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>

// PIC12F509
#pragma config OSC   = IntRC      // Oscillator Selection bits (internal RC oscillator)
#pragma config WDT   = OFF        // Watchdog Timer Enable bit (WDT disabled)
#pragma config CP    = OFF        // Code Protection bit (Code protection off)
#pragma config MCLRE = ON         // GP3/MCLR Pin Function Select bit (GP3/MCLR pinfunction is digital input, MCLR internally tied to VDD)

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000
#endif

#define ON  1
#define OFF 0
#define STEP 195
#define TMR0SET 256 - STEP      // TMR0はカウントアップして、256でオーバーフローする。
                                // TMR0の初期値を 256 - 195 = 61に設定すれば、
                                // 61からカウントアップして、195回目に256 オーバーフローする。

static unsigned char CountTMR0 = 0;

void Wait_50ms(void)

{
    // TMR0のプリスケーラ最大256を設定しても、約63msでオーバーフローする。
    // まず、基準となる1秒を作りたいので、50msでオーバーフローさせ、
    // オーバーフローを20回カウントしたら、1秒(1000ms)とする。
    
    TMR0 = TMR0SET; // Timer Clear
    while( TMR0 );  // Wait Counter Over Flow
    CountTMR0++;
}

void main(void)
{
    // 12F509 Settiing
    OSCCAL   = 0b01111110;  // 12F509 IntRC = 4MHz動作                            
    TRISGPIO = 0b00111000;  // GPIO Setting GP3-5:InPut GP0-2:OutPut
    GPIO     = 0b00000000;  // GPIO ALL OFF;
    OPTION   = 0b11000111;  // BIT0-2:TMR0 Prescaler 256
                            // T0CS: Timer0 Clock Source Select bit 0: FOSC /4
                            // PICは、基本的に1命令サイクル(=4クロック)で実行する
                            // ※ 1命令サイクル周期:使用クロックが4MHzなら(1/4000000) * 4 = 1 (μs)
                            // TMR0の初期値 = 256 - 任意の割り込み発生間隔 (s) /( 1命令サイクル周期 (s) × プリスケーラ比 )
                            // 12F509 は、TMR0割り込みやオーバーフロー時のフラグ保存(レジスタ)は無いらしいので、
                            // TMR0の値を常にチェックして、プログラム上で条件処理を行う必要がある。

    static unsigned char i = 0;
    static unsigned char j = 0;
    static unsigned char Count_GP4OFF = 0;

    while(1){
        
        j = 0;
        for ( i = 1 ; i <= 5; i++ ) {
            if ( ( GP0 == OFF) && ( GP5 == ON ) ) {
                j++;
            }
        }

        if ( ( GP0 == OFF) && ( j == 5 ) ){
            GP0 = ON;
            for ( i = 1; i <= 30; i++ ){
                GP1 = ON;
                __delay_ms(500);
                GP1 = OFF;
                __delay_ms(500);
            }
        }
        
        if ( ( GP0 == ON) && ( GP4 == OFF )){
            Wait_50ms();
            if (CountTMR0 == 20 ){
                // 50ms X 20 = 1秒間カウントしたら
                CountTMR0 = 0;
                Count_GP4OFF++;
                if ( Count_GP4OFF == 5 ){
                    // GP4が5秒間 OFFの場合、Haltと確定
                    Count_GP4OFF = 0;
                    for ( i = 1; i <= 10; i++ ){
                        GP1 = ON;
                        __delay_ms(500);
                        GP1 = OFF;
                        __delay_ms(500);
                    }
                    GP0 = OFF;
                    for ( i = 1; i <= 10; i++ ){
                        GP1 = ON;
                        __delay_ms(500);
                        GP1 = OFF;
                        __delay_ms(500);
                    }
                }
            }
        }
        else if ( GP0 == OFF) {
            CountTMR0 = 0;
            Count_GP4OFF = 0;
        }
    }
}