2016/08/26 home
PIC12F1822 スタンバイピー の制作
マイクのない無線機を手に入れたので電話機の受話器を改造してマイクを作ってみました。
PTTのスイッチを如何するかですが、PICの
mTouch(静電容量方式タッチセンサ)機能が内蔵されていますので、
基板上にタッチセンサ用のパターンを追加するだけで、タッチセンサが構築できます。
との事ですので実験してみました。

拡張ミッドレンジコアのPIC12F1822です。
最高32MHzの高速動作(32MHzオシレータ内蔵)。
また、nanoWatt XLP対応で、超低消費電力です。
・動作クロック:32MHz MAX
・プログラマブルクロックOSC内蔵(32kHz~32MHz)
・mTouch:4ch
・プログラムメモリ:2Kワード
・SRAM:128バイト
・EEPROM:256バイト
・I/Oポート:最大6本(内1本は入力専用)
・A/Dコンバータ:10ビットx4ch
・コンパレーター:1
・タイマー:8ビットx2,16ビットx1
・その他機能:ECCP、EUSART、MSSP、I2C(IIC)/SPI
・電源電圧:1.8V(16MHz MAX)~5.5V(32MHz MAX)

書き込み機はPIKKIT3です。

実験のための回路

このサイトを参考にしました。まずは確認
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 8000000 // delay用に必要(クロック8MHzを指定)
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000UL)))
#define ROBE_NUMBER 2 // 接続している電極の個数を指定
unsigned int CPS_data[2][ROBE_NUMBER] ; // 容量検知用データ
#pragma config FOSC = INTOSC // 内部クロック使用する(INTOSC)
#pragma config WDTE = OFF // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE = OFF // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP = OFF // プログラムメモリーを保護しない(OFF)
#pragma config CPD = OFF // データメモリーを保護しない(OFF)
#pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN = OFF // 外部クロック監視しない(OFF)
#pragma config WRT = OFF // Flashメモリーを保護しない(OFF)
#pragma config PLLEN = OFF // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV = HI // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF)
// 容量検知モジュールの初期値を読み込む処理
void CPS_Init()
{
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_ms(5) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
CPS_data[0][i] = (TMR1H*256) + TMR1L ;
CPS_data[1][i] = 0 ;
}
}
void CPS_ScanRobe() // 容量検知モジュールに接続されている電極の現在値を読み込む処理
{
unsigned int cap ;
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_ms(5) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
cap = (TMR1H*256) + TMR1L ;
if (cap <= (CPS_data[0][i]*0.9)) {
CPS_data[1][i] = cap ; // ONとする
} else {
CPS_data[1][i] = 0 ; // OFFとする
CPS_data[0][i] = cap ;
}
}
}
// 容量検知モジュールに接続されている電極の状態を調べる処理
// num : 調べる電極の番号を指定する
int CPS_StateRobe(int num)
{
if (num > ROBE_NUMBER) return( -1 ) ; // 数値指定エラー
if (CPS_data[1][num-1] == 0) return( 0 ) ; // 電極に触れていない
else return( 1 ) ; // 電極に触れている
}
void main() // メインの処理
{
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
ANSELA = 0b00000011 ; // アナログはAN0/AN1を使用し、残りAN2/AN3はデジタルI/Oに割当
TRISA = 0b00000011 ; // ピンRA0(AN0)/RA1(AN1)を入力、残りは出力に割当てる(RA3は入力専用)
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
// 容量検知モジュール(CPSM)の設定
CPSCON0 = 0b00001100 ; // オシレータは高範囲(高速の発信周波数)で利用する
// タイマー1の設定
T1CON = 0b11000001 ; // 容量検知オシレータでTIMER1をカウントする、プリスケーラカウント値 1:1
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
PEIE = 1 ; // 周辺装置割り込みを許可する
GIE = 1 ; // 全割り込み処理を許可する
// 容量検知モジュールの初期値を読み込む
CPS_Init() ;
while(1) {
// 容量検知モジュールの現在値を読み込む
CPS_ScanRobe() ;
// 電極1(CPS0)の状態でLED1を点灯させる処理
if (CPS_StateRobe(1) == 1) RA2 = 1 ; // LED1を点灯
else RA2 = 0 ; // LED1を消灯
// 電極2(CPS1)の状態でLED2を点灯させる処理
if (CPS_StateRobe(2) == 1) RA4 = 1 ; // LED2を点灯
else RA4 = 0 ; // LED2を消灯
}
}
なかなか良い感触です。触れるだけでPTTがONになります。
これでSWも不要になります。

ついでに昔流行ったスタンバイPも組み込みました。
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 8000000 // delay用に必要(クロック8MHzを指定)
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000UL)))
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000UL)))
#define ROBE_NUMBER 1 // 接続している電極の個数を指定
unsigned int CPS_data[2][ROBE_NUMBER] ; // 容量検知用データ
#pragma config FOSC = INTOSC // 内部クロック使用する(INTOSC)
#pragma config WDTE = OFF // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE = OFF // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP = OFF // プログラムメモリーを保護しない(OFF)
#pragma config CPD = OFF // データメモリーを保護しない(OFF)
#pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN = OFF // 外部クロック監視しない(OFF)
#pragma config WRT = OFF // Flashメモリーを保護しない(OFF)
#pragma config PLLEN = OFF // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV = HI // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF)
// 容量検知モジュールの初期値を読み込む処理
void CPS_Init()
{
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_us(5000) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
CPS_data[0][i] = (TMR1H*256) + TMR1L ;
CPS_data[1][i] = 0 ;
}
}
void CPS_ScanRobe() // 容量検知モジュールの電極の現在値を読み込む処理
{
unsigned int cap ;
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_us(5000) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
cap = (TMR1H*256) + TMR1L ;
if (cap <= (CPS_data[0][i]*0.9)) {
CPS_data[1][i] = cap ; // ONとする
} else {
CPS_data[1][i] = 0 ; // OFFとする
CPS_data[0][i] = cap ;
}
}
}
// 容量検知モジュールに接続されている電極の状態を調べる処理
// num : 調べる電極の番号を指定する
int CPS_StateRobe(int num)
{
if (num > ROBE_NUMBER) return( -1 ) ; // 数値指定エラー
if (CPS_data[1][num-1] == 0) return( 0 ) ; // 電極に触れていない
else return( 1 ) ; // 電極に触れている
}
void main() // メインの処理
{
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
ANSELA = 0b00000011 ; // アナログはAN0/AN1を使用し、残りAN2/AN3はデジタルI/Oに割当
TRISA = 0b00000011 ; // ピンRA0(AN0)/RA1(AN1)を入力、残りは出力に割当てる(RA3は入力専用)
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
CPSCON0 = 0b00001100 ; // オシレータは高範囲(高速の発信周波数)で利用する
T1CON = 0b11000001 ; // 容量検知オシレータでTIMER1をカウントする、プリスケーラカウント値 1:1
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
PEIE = 1 ; // 周辺装置割り込みを許可する
GIE = 1 ; // 全割り込み処理を許可する
CPS_Init() ; // 容量検知モジュールの初期値を読み込む
unsigned int j;
while(1) {
CPS_ScanRobe() ;
if (CPS_StateRobe(1) == 1)
{
RA5 = 0 ; // PTT on
while(CPS_StateRobe(1)==1) CPS_ScanRobe();
for (j=0;j<255;j++){ RA4 ^= 1 ;__delay_us(250); }
RA4 = 0 ;
RA5 = 1 ; // PTT off
}
else RA5 = 1 ; // PTT off
}
}
要のPTTのSWです。この線に触れるだけでPTT ON です。
ピーは2000サイクル 70mS位です。
2016//10/1
色々な無線機に使ってみると不具合が出てきたので改良しました。
1 電源電圧が5Vでない機種があったので抵抗と定電圧ダイオートを使って5Vに落としました。
2 マイクに電圧が掛かっていない機種があったので電圧を掛けました。
3 PTTに12Vが掛かっている機種があり逆流して誤動作したのでトランジスターを使って分離しました。
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#define _XTAL_FREQ 8000000 // delay用に必要(クロック8MHzを指定)
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000UL)))
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000UL)))
#define ROBE_NUMBER 1 // 接続している電極の個数を指定
unsigned int CPS_data[2][ROBE_NUMBER] ; // 容量検知用データ
#pragma config FOSC = INTOSC // 内部クロック使用する(INTOSC)
#pragma config WDTE = OFF // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE = OFF // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP = OFF // プログラムメモリーを保護しない(OFF)
#pragma config CPD = OFF // データメモリーを保護しない(OFF)
#pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN = OFF // 外部クロック監視しない(OFF)
#pragma config WRT = OFF // Flashメモリーを保護しない(OFF)
#pragma config PLLEN = OFF // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV = HI // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF)
// 容量検知モジュールの初期値を読み込む処理
void CPS_Init()
{
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_us(5000) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
CPS_data[0][i] = (TMR1H*256) + TMR1L ;
CPS_data[1][i] = 0 ;
}
}
void CPS_ScanRobe() // 容量検知モジュールの電極の現在値を読み込む処理
{
unsigned int cap ;
int i ; // 接続している電極の分だけ繰り返す
for (i=0 ; i<ROBE_NUMBER ; i++) {
CPSCON1 = i ; // 読み込むチャンネルを設定する
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
CPSON = 1 ; // 容量検知モジュール開始
__delay_us(5000) ; // 5msの間タイマー1をカウントさせる
// 容量検知モジュールの値を読み込む
CPSON = 0 ; // 容量検知モジュール停止
cap = (TMR1H*256) + TMR1L ;
if (cap <= (CPS_data[0][i]*0.9)) {
CPS_data[1][i] = cap ; // ONとする
} else {
CPS_data[1][i] = 0 ; // OFFとする
CPS_data[0][i] = cap ;
}
}
}
// 容量検知モジュールに接続されている電極の状態を調べる処理
// num : 調べる電極の番号を指定する
int CPS_StateRobe(int num)
{
if (num > ROBE_NUMBER) return( -1 ) ; // 数値指定エラー
if (CPS_data[1][num-1] == 0) return( 0 ) ; // 電極に触れていない
else return( 1 ) ; // 電極に触れている
}
void main() // メインの処理
{
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
ANSELA = 0b00000011 ; // アナログはAN0/AN1を使用し、残りAN2/AN3はデジタルI/Oに割当
TRISA = 0b00000011 ; // ピンRA0(AN0)/RA1(AN1)を入力、残りは出力に割当てる(RA3は入力専用)
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
CPSCON0 = 0b00001100 ; // オシレータは高範囲(高速の発信周波数)で利用する
T1CON = 0b11000001 ; // 容量検知オシレータでTIMER1をカウントする、プリスケーラカウント値 1:1
TMR1H = 0 ; // タイマー1の初期化
TMR1L = 0 ;
PEIE = 1 ; // 周辺装置割り込みを許可する
GIE = 1 ; // 全割り込み処理を許可する
CPS_Init() ; // 容量検知モジュールの初期値を読み込む
unsigned int j;
while(1) {
CPS_ScanRobe() ;
if (CPS_StateRobe(1) == 1)
{
RA5 = 1 ; // PTT on
while(CPS_StateRobe(1)==1) CPS_ScanRobe();
for (j=0;j<255;j++){ RA4 ^= 1 ;__delay_us(250); }
RA4 = 0 ;
RA5 = 0 ; // PTT off
}
else RA5 = 0 ; // PTT off
}
}
ソースはRA5を反転しました。
回路図はTR、D、R の追加