今となってはスペック不足となって、使わなくなったBananaPI M1。
せっかくなので、就寝時のオーディオプレイヤーとして、色々と機能を持たせて製作したい。
そもそも著者は、就寝時に音楽をかけながら眠りにつくので、少し特殊な使い方(要件)となるので、
備忘として残しておく
今回は色々と弄ったり、PICを使って利便性を上げたりなどしていて、資料が多くなる事が予想されるので、
章立てで進めて行くことにする。
とりあえず、音が出る事を確認。(mp3 or wav ファイル)
ヘッドレス状態で
PC用スピーカーとBPI用スピーカーの2台を置く事は、置き場所の問題もさることながら、
アンプ付きスピーカーである為、USB電源ではあるけど、配線周りもごちゃごちゃする。
また、節電対策としても(チューニングすれば可能かもしれないが)、PCやBPIのシャットダウン状態では、
USB電源は供給されたままであり、スピーカー電源が入りっぱなしだ。
PICやリレーを使って、スピーカー1台を共用する事にする。
今回の要件では、PCとBPIを同時に音を鳴らす必要は無い。
・ スピーカーの電源は、
・ スピーカーへの音声出力は、
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;
}
}
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;
}
}
}