abc950ではハードディスクやUSBメモリ等の低レベルフォーマットができるツールを紹介しましたが、MiniTool Partition Wizardはパーテションサイズの変更などを行いたい時のツールです。
同ソフトには、無料版(Free)と有料版(Pro)があり、無料版は個人ユーザー向けのパーティション管理ソフトです。なお、動作環境はWindows11/10/8.1/8/7。
紹 介: tekunotes
入 手: minitool
P PIC16F13145 (8) 〜7segLEDの応用(ADC利用の電圧計)
by fjk
abc950まで、小数点付数値を2桁7セグメントLEDで表示できるようになったので、PICのADC入力を利用し、簡単な電圧計を作ってみる。
なお、電圧データの小数点は固定で、データ確認のためUSBシリアルでパソコンにもデータを送信
- 【ハード】
- 「abc950のおまけ」回路(小数点付2桁7segLEDとVR付)をそのまま使用
- 【ソフト】
- RA2に接続したVRを回すと、ADCにより電圧を10ビットデータとして取得し、そのデータを電圧値に変換、さらにBCD変換後、2桁7セグメントLEDに表示(小数点表示あり)する。
また、USBシリアル端末から数値データを送信すると、下2桁の数値をBCD値として2桁7セグメントLEDに2秒間だけ表示(小数点表示なし)する。
- <使用するMCCデバイス>
- ・Clock:
- HFINTOSC=32MHz、Divider=1
- ・UART1:
- abc945と同じ。speed=115200bps、割込有り(Driver名がUARTなので注意)
- ・TMR0:
- abc950と同じ。period =8mS
- ・CLB1:
- abc950と同じ。abc950-7seg2B-DM.clbを利用
- ・CLC:
- abc950と同じ(jk-FF使用)
- ・ADC:
- 下記「ADCの設定」を参照

MCC:System (clock=32MHz)、TMR0 (Period=8mS)
|
- <ADCの設定> (下記条件以外はdefaultのまま)
hardwareSetting
- ResultAlignment:
- right (右詰め)
- Positive input Channel:
- ANA2(PinGrid、Pinsで確認)
- PositivVoltageReference:
- VDD(上限は5V)
ComputerationSettings
- Computation Mode:
- Basic_mode
ADC Clock Setting
- Clock Source:
- FOSC
- Clock Divider:
- FOSC/32 (1MHz= 1μs)
CVD Settinng: (CVDを使用しない)
Interrupt Settings: (割込を使用しない)
Analog Channels: (ADCのグループ化をしない)
|

ADC設定
|
- <ADCデータ(aDat)を最大5Vの電圧データ(vVal)に変換>
- ・2桁BCD用:
- vVal = aDat × 50 / 1023 (vValの最大値が50)
- ・3桁BCD用:
- vVal = aDat × 500 / 1023 (vValの最大値が500)
- ・4桁BCD用:
- vVal = aDat × 5000 / 1023 (vValの最大値が5000)
- 浮動小数点演算を使用してもよいが、符号無し32bit整数に変換して、32bit整数演算。
- uint16_t vVal = (uint16_t)((uint32_t)aDat * 500UL / 1023UL); // 3桁の場合
- ただし、10bit2進数から2桁BCDなら16bit計算で桁溢れしないので、下記演算(切捨)が可能
- uint16_t vVal = (uint16_t)(aDat * 50 / 1023); // 1,023×50=51,150 < 65,536
- <バイナリーデータをBCDへ変換>
vValデータは2進数なので、7segLEDに表示するにはBCDに変換する必要がある。
BCDへの変換方法は多数あるが、XC8に標準実装されている以下の div() 関数を使うのが便利そう
- div_t div (int numer, int denom)
- なお、div_t はstdlib.hで以下のように定義されている
typedef struct{
int quot; // quotient(商)
int rem; // remainder(余り)
} div_t;
|
- 最大4桁(9999)のBCDを得る関数の例
#include <stdio.h>
#include <stdlib.h>
uint16_t bin2bcd(int d) { // 9999まで(下4桁のみ有効)
div_t qr;
int i, bcd = 0;
for (i = 0; i < 4; ++i, d = qr.quot) {
qr = div(d, 10); // 商と余りを得る
bcd += qr.rem << (i * 4); // 余りをi桁分を左シフトして加算
}
return (uint16_t)bcd;
}
|
【テスト結果とトラブル】
AD変換データはVRを回すと0.0−5.0Vで表示され、シリアル入力も指示した数値をチャンと表示した。
しかし、シリアル出力(Tx)がUSBターミナルに表示されない(RA5から信号が出ていない[RA4に変更しても同じ])。
そこで、Tx端子をRB6に変更してみたところ、正常にシリアル送信できた(abc945ではRA4を使っても問題なかったのに・・)

UART配線を変更(Tx=RB6)
|
ADCデータ(電圧)を表示中
|
| ▼abc951-13145.c(zip)
|
/******************************(abc951-13145.c)******
* ADCデータを電圧に変換後7segに表示
*--------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "mcc_generated_files/system/system.h"
//===== シリアル関係 ================
#define EU_BFSIZE 80
char RBuf[EU_BFSIZE]; // 受信データバッファ
uint8_t EU_Flg = 0; // 受信フラグ
//----- シリアル受信割り込み関数
void myEusart(void) {
static uint8_t sIdx = 0; // 受信データポインタ
char ch; // 受信文字
if(EUSART1_IsRxReady()){ // 受信データ有り?
ch = EUSART1_Read(); // データ受信
EUSART1_Write(ch); // エコーバック
if((ch == 0x0a)||(ch == 0x0d)){ // 改行処理
RBuf[sIdx] = 0; // 文字列終端文字
sIdx = 0; // ポインタを初期化
EU_Flg = 1; // 受信フラグON
}else if((ch == 0x08)&&(sIdx > 0)){ // BS処理
sIdx--;
}else{ // 上記以外の文字
if(sIdx < EU_BFSIZE){ // バッファサイズ以内?
RBuf[sIdx++] = ch; // バッファにデータ格納
}
}
}
}
//----- 文字列シリアル出力(改行なし)
void EU_print(char * msg) {
while(*msg != 0) {
while(!EUSART1_IsTxReady())
; // データ送信済確認
EUSART1_Write(*msg++); // 1文字送信
}
}
//----- 文字列シリアル出力(改行付き)
void EU_puts(char *msg) {
EU_print( msg );
EU_print("\n");
}
//===== 4桁BCD変換関数 ==============
uint16_t bin2bcd(int d) { // 最大9999(下位4桁)
div_t qr;
int i, bcd = 0;
for (i = 0; i < 4; ++i, d = qr.quot) {
qr = div(d, 10); // 商・余りを得る
bcd += qr.rem << (i * 4); // i 桁分左シフト後、加算
}
return (uint16_t)bcd;
}
//========= main ===================
int main(void) {
uint16_t aDat; // ADCデータ
uint16_t vVal; // 電圧データ
uint16_t dat; // 2進数データ
uint16_t bcd; // 4桁BCD数
char sBf[20]; // 出力文字列
SYSTEM_Initialize(); // 初期化
EUSART1_RxCompleteCallbackRegister(myEusart);
// 割り込みハンドラー設定
INTERRUPT_GlobalInterruptEnable(); // 割り込み許可
INTERRUPT_PeripheralInterruptEnable();
EU_puts("== Test ADC & 7seg ==");
ADC_ConversionStart(); // ADC変換開始
while(1) {
//---- USB Serial -----------
if(EU_Flg){ // 受信データ有?
EU_Flg = 0; // 受信フラグクリア
dat = (uint16_t)atoi(RBuf); // 受信文字列を数値に
bcd = bin2bcd((int)dat); // 4桁bcdに変換
sprintf(sBf, "%d -> %x", dat,bcd); // 表示文字列へ
EU_puts(sBf); // シリアル送信
CLB1_SWIN_Write16(bcd); // 7seg表示
__delay_ms(2000); // 2秒間表示
}
//---- ADC -------------------
if(ADC_IsConversionDone()){ // ADC変換が終了?
aDat = (uint16_t)ADC_ConversionResultGet();
vVal = (uint16_t)(aDat * 50 / 1023);
bcd = bin2bcd((int)vVal); // 4桁BCDに変換
sprintf(sBf,"%d -> %x", vVal,bcd); // 表示文字列へ
EU_puts(sBf); // シリアル送信
bcd |= 0x8000; // 小数点表示を加える
CLB1_SWIN_Write16(bcd); // 7seg表示
ADC_ConversionStart(); // ADC変換開始
__delay_ms(500); // ADC変換間隔調整
}
}
}
|
※プログラムのリストをハイライト付きのスタイルで見る場合はここをクリック
※ 本レポートの参考・利用は、あくまでも自己責任でお願いします。