こんにちは。Yukiです。
今回は、前回設計したオーディオミキサーのはんだ付けをしていこうと思います。
これは前の記事の続きです。ぜひご覧ください。
基板

今回は46×46の基板(穴間のサイズは40×40)という大きさにするために、両面実装にしました。今後はこの大きさで様々な回路を統一し、重ねて置けるような設計にしようと考えています。
PIC12F1822が搭載されている理由
このマイコンで電圧監視しています。今回は、ニッケル水素充電池を使用する想定で作っており、過放電を起こすと面倒なので、このような設計にしました。仕組みとしては単純で、PIC内部のFVRをADCの基準電圧とします。後は、電源電圧を分圧した電圧をADCしているだけです。
電源電圧の分類としては、2段階にしています。
- 充電を促す1段階目(電源供給あり)
- 電池交換させる2段階目(電源供給ストップ)
本当はメモリー効果などの理由でちゃんと放電させてから、充電したほうが良いらしいですが、まあ大丈夫でしょう。最近はメモリー効果も性能向上で薄れているらしいですし…。
プログラミングする
まずはconfig設定をします。
// CONFIG1
#pragma config FOSC = INTOSC // 内蔵オシレータ使用
#pragma config WDTE = OFF // ウォッチドックタイマー無効
#pragma config PWRTE = ON // パワーアップタイマー有効
#pragma config MCLRE = OFF // リセットピンでのリセット無効
#pragma config CP = OFF // コードプロテクション無効
#pragma config CPD = OFF // データコードプロテクション無効
#pragma config BOREN = ON // ブラウンアウトリセット有効
#pragma config CLKOUTEN = OFF // クロック出力無効
#pragma config IESO = ON // クロックスイッチオーバー有効
#pragma config FCMEN = ON // フェイルセーフクロックモニター有効
// CONFIG2
#pragma config WRT = OFF // フラッシュメモリ書き込みプロテクション無効
#pragma config PLLEN = OFF // PLL(x4)無効
#pragma config STVREN = ON // スタックオーバーフロー/アンダーフロー時リセット
#pragma config BORV = LO // ブラウンアウトリセット リセット電圧ロー
#pragma config DEBUG = OFF // ICSPデバック無効
#pragma config LVP = ON // 低電圧書き込み有効今回は上のように設定しました。消費電力の関係で、今回は1MHzで動作させます。
次に、各種インクルードや定数です。
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 1000000
#define BATT_REF 0
#define PWR_SW LATAbits.LATA2
#define LED LATAbits.LATA4
#define LV1_VOLT 110 //LEDが光る一段回目の警告のアナログ値
#define LV2_VOLT 100 //電源をカットするアナログ値_XTAL_FREQは__delay_msマクロなどを使用するときに必要です。BATT_REFは何で書いたのかよくわかりません。(おそらく、電池の電圧が接続されている入力ピンを指しているんだと思います)
後は、出力関係の定数宣言ですね。最後に、LV1_VOLTが警告の電圧(実際にはADCの出力値)
LV2_VOLTが電源をカットする値になります。
次に、オシレータを1MHzにする関数です。
//マイコンの動作周波数を1MHzにする
void OSC_1MHz(void){
OSCCONbits.SPLLEN = 0; //PLL無効
OSCCONbits.IRCF = 0b1011; //内蔵オシレータ1MHz
}PLLを無効化して、内蔵オシレータを1MHzにします。次に、GPIOの設定とADCの設定 FVRの設定をします。
//GPIOの入出力設定
void GPIO_init(void){
TRISAbits.TRISA0 = 1; //BATT_REF
TRISAbits.TRISA2 = 0; //PWR_SW
TRISAbits.TRISA4 = 0; //LED
ANSELAbits.ANSA0 = 1; //PA0 アナログ入力
}
void ADC_init(void){
ADCON1bits.ADCS = 0b110; //Fosc/64を使用
ADCON1bits.ADPREF = 0b11; //VrefをFVRに接続
ADCON1bits.ADCS = 0b00000; //AN0に接続
ADCON0bits.ADON = 1; //ADC有効
}
void FVR_2048_init(void){
FVRCONbits.ADFVR = 0b10; //ADC用に2.048V
FVRCONbits.FVREN = 1; //FVR有効
while(FVRCONbits.FVRRDY == 0); //FVRが使用可能になるまで
}GPIO_initは入出力などの関係です。TRISAレジスタで入出力設定。ANSELAレジスタでアナログ入力設定です。
ADC_initは、ADCの初期化関数です。今回は、PA0以外のADC入力はしません。ということで、AN0(PA0)に接続しておきます。今回はFosc/64というあえて遅くしてあります。本当はあれですが、まあ大丈夫でしょう。
FVR_2048_init関数は、FVRを2.048Vにする関数です。一応FVRが有効になるまで、待機しています。
次に、ADC_result関数です。
int ADC_result(void){
ADCON0bits.GO = 1; //変換開始
while(ADCON0bits.GO == 1); //ADCが完了するまで待機
return ADRESH; //ADCの結果を返却
}ADRESHのみ(8bit)を返却しています。計算式としては、
$$電圧 V_{ADC} = (\frac{2.048}{255}) × ADC値$$
となります。式変形をすると
$$ADC値 = \frac{255}{2.048} V_{ADC}$$
となります。
今回は、
最後に、main文です。
int main(int argc, char** argv) {
OSC_1MHz();
GPIO_init();
FVR_2048_init();
ADC_init();
__delay_ms(10); //いろいろ安定するまで待つ
LED = 1;
__delay_ms(1000);
LED = 0;
while(1){
int batt_volt = ADC_result();
//float batt_v = (2.048/1023) * batt_volt;
if(batt_volt < LV2_VOLT){
//電源遮断
PWR_SW = 1; //CH217K シャットダウン
LED = 1;
__delay_ms(10);
LED = 0;
}else if(batt_volt < LV1_VOLT){
//警告用LED
LED = 1;
}else{
//通常時
PWR_SW = 0;
LED = 0;
}
__delay_ms(1000);
}
return (EXIT_SUCCESS);
}最後に、全てのソースコードを合わせたものを乗せておきます。コピペ等にお使いください。
/*
* File: newmain.c
* Author: denshi1996(yuki)
*
* Created on 2024/07/02, 11:58
*/
// CONFIG1
#pragma config FOSC = INTOSC // 内蔵オシレータ使用
#pragma config WDTE = OFF // ウォッチドックタイマー無効
#pragma config PWRTE = ON // パワーアップタイマー有効
#pragma config MCLRE = OFF // リセットピンでのリセット無効
#pragma config CP = OFF // コードプロテクション無効
#pragma config CPD = OFF // データコードプロテクション無効
#pragma config BOREN = ON // ブラウンアウトリセット有効
#pragma config CLKOUTEN = OFF // クロック出力無効
#pragma config IESO = ON // クロックスイッチオーバー有効
#pragma config FCMEN = ON // フェイルセーフクロックモニター有効
// CONFIG2
#pragma config WRT = OFF // フラッシュメモリ書き込みプロテクション無効
#pragma config PLLEN = OFF // PLL(x4)無効
#pragma config STVREN = ON // スタックオーバーフロー/アンダーフロー時リセット
#pragma config BORV = LO // ブラウンアウトリセット リセット電圧ロー
#pragma config DEBUG = OFF // ICSPデバック無効
#pragma config LVP = ON // 低電圧書き込み有効
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define _XTAL_FREQ 1000000
#define BATT_REF 0
#define PWR_SW LATAbits.LATA2
#define LED LATAbits.LATA4
#define LV1_VOLT 110 //LEDが光る一段回目の警告のアナログ値
#define LV2_VOLT 100 //電源をカットするアナログ値
void OSC_1MHz(void); //1MHz動作
void GPIO_init(void); //入出力設定
void ADC_init(void); //ADC設定
void FVR_2048_init(void); //固定参照電圧 2.048V
int ADC_result(void); //AN0に接続されたADCの結果を返却
/*
*
*/
int main(int argc, char** argv) {
OSC_1MHz();
GPIO_init();
FVR_2048_init();
ADC_init();
__delay_ms(10); //いろいろ安定するまで待つ
LED = 1;
__delay_ms(1000);
LED = 0;
while(1){
int batt_volt = ADC_result();
//float batt_v = (2.048/1023) * batt_volt;
if(batt_volt < LV2_VOLT){
//電源遮断
PWR_SW = 1; //CH217K シャットダウン
LED = 1;
__delay_ms(10);
LED = 0;
}else if(batt_volt < LV1_VOLT){
//警告用LED
LED = 1;
}else{
//通常時
PWR_SW = 0;
LED = 0;
}
__delay_ms(1000);
}
return (EXIT_SUCCESS);
}
//マイコンの動作周波数を1MHzにする
void OSC_1MHz(void){
OSCCONbits.SPLLEN = 0; //PLL無効
OSCCONbits.IRCF = 0b1011; //内蔵オシレータ1MHz
}
//GPIOの入出力設定
void GPIO_init(void){
TRISAbits.TRISA0 = 1; //BATT_REF
TRISAbits.TRISA2 = 0; //PWR_SW
TRISAbits.TRISA4 = 0; //LED
ANSELAbits.ANSA0 = 1; //PA0 アナログ入力
}
void ADC_init(void){
ADCON1bits.ADCS = 0b110; //Fosc/64を使用
ADCON1bits.ADPREF = 0b11; //VrefをFVRに接続
ADCON1bits.ADCS = 0b00000; //AN0に接続
ADCON0bits.ADON = 1; //ADC有効
}
void FVR_2048_init(void){
FVRCONbits.ADFVR = 0b10; //ADC用に2.048V
FVRCONbits.FVREN = 1; //FVR有効
while(FVRCONbits.FVRRDY == 0); //FVRが使用可能になるまで
}
int ADC_result(void){
ADCON0bits.GO = 1; //変換開始
while(ADCON0bits.GO == 1); //ADCが完了するまで待機
return ADRESH; //ADCの結果を返却
}電圧調整
先ほど式を書きましたが、もう一度出します。
$$電圧 V_{ADC} = (\frac{2.048}{255}) × ADC値$$
$$ADC値 = \frac{255}{2.048} V_{ADC}$$
今回は、10kΩと30kΩで分圧しています。よって、
$$\frac{10k}{10k+30k} = \frac{1}{4}$$
よって、電圧は1/4になります。ニッケル水素充電池系はこまめに充電する方が長持ちする(と信じている)ので、3.5V程度で警告、3.2Vで供給ストップしようと思います。
上の式から、3.5Vは分圧後0.875V, 3.2Vは分圧後0.8Vになります。
電圧→ADCに変換する式
$$ADC値 = \frac{255}{2.048} V_{ADC}$$
から、 0.875V→108.95≒109, 0.8V→99.61≒100になります。上のプログラムでは、キリが良いように、110, 100としています。
実際の動作の様子

こんな感じです。

入力電圧が3.6Vのときは、LEDは点灯せず、スイッチがONになっています。

電源電圧が下がってきたら、LEDが点灯します。3.48Vくらいから点灯し始めます。このときも、出力はONです。

電源電圧が3.2Vくらいです。LEDがパルス的に光るようになります。電源供給はストップされます。

電源出力端子は、このように別の基板につなげて使います。現在使っていない2ピンのXHコネクタの場所も、VccとGNDとして使えます。3ピンのXHコネクタは、仮想GNDが接続してあります。(Vcc/2, 仮想GND, GNDのようになっています)
ちなみに左側の基板は、ECMで作ったマイク回路です。(ライン入力レベルまで増幅出来るように設計したのですが、設計失敗…。ゴミになりました)
総評
個人的に結構よく出来たと思います。ただ、3入力はいらなかったかな?次は2入力で、音量調整(正確には入力の音量調整)ができれば良いかなと思っています。まあとりあえずプロトタイプとして…。
販売
余った基板をBOOTHで販売しようと思います。
準備ができ次第、リンクを貼ろうと思います。

販売始めました。まあ売れないとは思いますが、ぜひどうぞ。



コメント